diff --git a/.github/workflows/niceplots.yml b/.github/workflows/niceplots.yml
index 0bf5bc7..edb00c9 100644
--- a/.github/workflows/niceplots.yml
+++ b/.github/workflows/niceplots.yml
@@ -8,7 +8,6 @@ on:
tags:
- v*.*.*
pull_request:
- branches: [main]
jobs:
black:
@@ -21,27 +20,64 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.7, 3.8, 3.9]
- numpy-version: ["1.16.6", "1.18.5"]
- mpl-version: ["3.2.*", "3.3.*", "3.4.*"]
+ python-version: ["3.8", "3.9", "3.10"]
+ numpy-version: ["1.19.*", "1.21.*", "1.24.*"]
+ mpl-version: ["3.4.*", "3.6.*"]
+ exclude:
+ - python-version: "3.9"
+ numpy-version: "1.19.*"
+ - python-version: "3.10"
+ numpy-version: "1.19.*"
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- - name: Install dependencies
+ - name: Install apt dependencies
+ uses: awalsh128/cache-apt-pkgs-action@latest
+ with:
+ packages: fonts-cmu nodejs npm
+ version: 1.0
+ - name: Install node dependencies
+ run: |
+ sudo npm install -g odiff-bin
+ - name: Install Prompt font
+ run: |
+ mkdir Prompt
+ cd Prompt
+ wget "https://fonts.google.com/download?family=Prompt" -O Prompt.zip
+ unzip Prompt.zip
+ mkdir ~/.fonts
+ cp *.ttf ~/.fonts
+ - name: Install python dependencies
run: |
- sudo apt-get install fonts-cmu
pip install --upgrade pip wheel
pip install numpy==${{ matrix.numpy-version }}
pip install matplotlib==${{ matrix.mpl-version }}
pip install .
- name: Test examples
run: |
- # this is very janky for now
cd examples
bash testExamples.sh
+ - name: Compare against reference images
+ if: ${{ success() && matrix.python-version == '3.10' && matrix.numpy-version == '1.24.*' && matrix.mpl-version == '3.6.*' }}
+ run: |
+ cd examples
+ bash ImageComparisonTest.sh
+ - name: Upload examples if failed
+ uses: actions/upload-artifact@v3
+ if: ${{ failure() && matrix.python-version == '3.10' && matrix.numpy-version == '1.24.*' && matrix.mpl-version == '3.6.*' }}
+ with:
+ name: Examples
+ path: examples/
+ # - name: Upload new reference images
+ # if: ${{ github.event_name == 'push' && success() && matrix.python-version == '3.10' && matrix.numpy-version == '1.24.*' && matrix.mpl-version == '3.6.*' }}
+ # uses: stefanzweifel/git-auto-commit-action@v4
+ # with:
+ # file_pattern: 'examples/ref/*.png'
+ # commit_message: Update reference images
+
# --- publish to PyPI
pypi:
diff --git a/.gitignore b/.gitignore
index b99349d..2bed3e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,15 @@
-# Ignore any pdf's or png's except in the examples folder
+# Ignore any figures, except the reference images used for testing
*.pdf
-!examples/*.pdf
*.png
-!examples/*.png
+!examples/ref/*.png
+*.svg
.vscode/
+# Ignore generated doc files
+doc/_build/
+doc/auto_examples/
+
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
@@ -152,4 +156,4 @@ dmypy.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
-*.code-workspace
\ No newline at end of file
+*.code-workspace
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 6a6dd0f..94986ab 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -9,9 +9,19 @@ version: 2
build:
os: ubuntu-20.04
tools:
- python: "3.9"
+ python: "3.10"
apt_packages:
- fonts-cmu
+ # Install the fonts for the james style
+ jobs:
+ pre_install:
+ - mkdir Prompt
+ - cd Prompt
+ - wget "https://fonts.google.com/download?family=Prompt" -O Prompt.zip
+ - unzip Prompt.zip
+ - mkdir ~/.fonts
+ - cp *.ttf ~/.fonts
+ - cd ..
# Build documentation in the docs/ directory with Sphinx
sphinx:
diff --git a/README.md b/README.md
index 68055d0..d99c9be 100644
--- a/README.md
+++ b/README.md
@@ -7,53 +7,74 @@
[![PyPI - Downloads](https://img.shields.io/pypi/dm/niceplots)](https://pypi.org/project/niceplots/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
-
-
+
+
+
-### How do I install?
+## How do I install?
-Niceplots can be pip installed directly from PyPI
+NicePlots can be pip installed directly from PyPI
```shell
pip install niceplots
```
-#### If you want to make changes
+### If you want to make changes
* Clone this repository, then enter the folder in the command line terminal.
* Enter `pip install -e .` within the `niceplots` folder.
-#### Font installation (optional)
+### Font installation (optional)
-Niceplots will try and use the [computer modern bright](https://tug.org/FontCatalogue/computermodernbright/) font for the best looking plots so be sure to install it as a system font if you want to recreate the style of the plots above.
-Otherwise, niceplots will still work but revert back to the matplotlib default sans-serif font, DejaVu Sans.
+NicePlots styles use fonts that do not ship with most operating systems, so you'll need to install them separately.
+If they are not installed, matplotlib will revert back to its default sans-serif font, DejaVu Sans.
+
+The font used by each style is as follows:
+- doumont-light (default niceplots): CMU Bright
+- doumont-dark: CMU Bright
+- james-dark: Prompt
+- james-light: Prompt
+
+Install the fonts on your system and then delete Matplotlib's font cache, which is located in `~/.cache/matplotlib` by default on most operating systems.
+Matplotlib will rebuild the font cache next time it is run and (hopefully) find the new fonts.
+
+#### CMU Bright (doumont-light and doumont-dark)
+
+The computer modern bright font can be downloaded from [this link](https://tug.org/FontCatalogue/computermodernbright/).
+Alternatively, on Ubuntu, the font can be installed with the following commands:
-To install the font on Ubuntu, run the following commands:
```
sudo apt-get update
sudo apt-get install fonts-cmu
```
Arch linux users can get the font by installing the `otf-cm-unicode` package from AUR.
-If niceplots doesn't recognize the font, it might be necessary to delete Matplotlib's font cache file from its location on your computer, likely in `~/.cache/matplotlib`
+#### Prompt (james-dark and james-light)
+
+The Prompt font can be download from [Google Fonts](https://fonts.google.com/specimen/Prompt).
+
+## How do I get set up?
+
+* `import matplotlib.pyplot as plt` and `import niceplots` at the top of a file where you would like to use any function defined in this package.
+* Use `plt.style.use(niceplots.get_style())` to set some defaults for nice-looking plots. You can also try passing different styles to `get_style()`, such as NicePlots' `"james-dark"` or any of matplotlib's styles (see the function's documentation for a full list of available NicePlots styles).
+* Take advantage of NicePlots' helper functions, including (but not limited to) `adjust_spines`, `horiz_bar`, and `plot_nested_pie`, which are all documented in the [examples gallery](https://mdolab-niceplots.readthedocs-hosted.com/en/latest/auto_examples/index.html).
+* Admire your beautiful data.
-### How do I get set up?
+## Do you have docs?
-* Use `import niceplots` at the top of a file where you would like to use any function defined in this package.
-* Use `niceplots.setRCParams()` to set some matplotlib defaults for nice looking plots. Set `dark_mode=True` and `set_background_color=True` to make plots with a dark background.
-* Use `niceplots.All()` after all the plot commands to apply the niceplot standards on the figure.
-* To use the Matlab colormap "parula", execute `from niceplots import parula` then use `parula.parula_map` as your colormap within your plotting script. See the contour plot example code for an example of this.
+Sort of, you can find our examples gallery and API documentation [here](https://mdolab-niceplots.readthedocs-hosted.com/en/latest)
-### Do you have docs?
+## Help, my old NicePlots code doesn't work anymore!
-Sort of, you can find our examples gallery and api documentation [here](https://mdolab-niceplots.readthedocs-hosted.com/en/latest)
+We made a couple of changes to the API in version 2.0.0, most of them can be fixed with a simple find and replace.
+Check the [release notes](https://github.com/mdolab/niceplots/releases/tag/v2.0.0) for more details.
-### Contribution guidelines
+## Contribution guidelines
* Make any changes you see fit. Please fork your own version and submit a pull request.
-### Who do I talk to?
+## Who do I talk to?
* Alasdair Gray, alachris@umich.edu
* Eytan Adler, eytana@umich.edu
diff --git a/doc/conf.py b/doc/conf.py
index b7c0ca2..e5b710f 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -1,4 +1,46 @@
from sphinx_mdolab_theme.config import *
+from glob import glob
+import shutil
+import os
+from sphinx_gallery.scrapers import figure_rst
+from pathlib import PurePosixPath
+
+
+class svgScraper(object):
+ """This is a custom scraper for sphinx-gallery that allows us to use the svg files written by our examples. It is
+ almost entirely copied from the PNGScraper shown at:
+ https://sphinx-gallery.github.io/dev/advanced.html#example-2-detecting-image-files-on-disk
+ """
+
+ def __init__(self):
+ self.seen = set()
+
+ def __repr__(self):
+ return "svgScraper"
+
+ def __call__(self, block, block_vars, gallery_conf):
+ # Find all svg files in the directory of this example.
+ path_current_example = os.path.dirname(block_vars["src_file"])
+ svgs = sorted(glob(os.path.join(path_current_example, "*.svg")))
+
+ # Get the name of the current example, e.g if the file is called "plot_nested_pie_chart.py",
+ # then example_name = "nested_pie_chart"
+ example_name = os.path.splitext(os.path.basename(block_vars["src_file"]))[0].split("plot_")[0]
+
+ # Iterate through svgs, copy them to the sphinx-gallery output directory
+ image_names = list()
+ image_path_iterator = block_vars["image_path_iterator"]
+ for svg in svgs:
+ if svg not in self.seen and example_name.lower() in svg.lower():
+ self.seen |= set(svg)
+ this_image_path = image_path_iterator.next()
+ image_path = PurePosixPath(this_image_path)
+ image_path = image_path.with_suffix(".svg")
+ image_names.append(image_path)
+ shutil.move(svg, image_path)
+ # Use the `figure_rst` helper function to generate rST for image files
+ return figure_rst(image_names, gallery_conf["src_dir"])
+
# -- Project information -----------------------------------------------------
project = "niceplots"
@@ -8,4 +50,5 @@
sphinx_gallery_conf = {
"examples_dirs": "../examples", # path to your example scripts
"gallery_dirs": "auto_examples", # path to where to save gallery generated output
+ "image_scrapers": (svgScraper(),),
}
diff --git a/doc/index.rst b/doc/index.rst
index 652c114..386e2eb 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -3,7 +3,7 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-Welcome to niceplots's documentation!
+Welcome to NicePlots's documentation!
=====================================
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 289f2fc..002bfca 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -1,3 +1,3 @@
sphinx_mdolab_theme
sphinx-gallery
-matplotlib>=3.4
\ No newline at end of file
+matplotlib>=3.6
diff --git a/examples/ImageComparisonTest.sh b/examples/ImageComparisonTest.sh
new file mode 100644
index 0000000..0f59243
--- /dev/null
+++ b/examples/ImageComparisonTest.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# This script compares the images generated by the example scripts to a set of reference images
+
+test_passed=true # Kepp track of whether any tests fail
+
+# First check that there aren't any images produced by the examples that don't have a reference image
+for f in *.png
+do
+ if ! test -f "ref/$f"; then
+ echo ""
+ echo "$f doesn't have a reference image to compare to"
+ test_passed=false
+ fi
+done
+
+mkdir -p diffs
+
+# For each image in the ref directory, check that there is a corresponding image in the current directory and then compare them
+for f in ref/*.png
+do
+ echo ""
+ base_name=$(basename ${f})
+
+ if test -f "$base_name"; then
+ filename_no_ext="${base_name%.*}"
+ echo "Comparing $base_name and $f"
+ odiff --antialiasing --threshold=0.1 --diff-mask $base_name $f diffs/${filename_no_ext}-diff.png
+ if [ $? -ne 0 ]; then
+ test_passed=false
+ fi
+ else
+ echo "Couldn't find image to compare against $f"
+ test_passed=false
+ fi
+done
+
+echo ""
+echo "==========================="
+if [ "$test_passed" = true ] ; then
+ echo "All comparisons passed"
+ echo "==========================="
+ exit 0
+else
+ echo "Some comparisons failed"
+ echo "==========================="
+ exit 0 # In future (if we can make the image comparison more robust) we should exit with a non-zero exit code (e.g. 1)
+fi
diff --git a/examples/ParulaContours.png b/examples/ParulaContours.png
deleted file mode 100644
index f03bb9d..0000000
Binary files a/examples/ParulaContours.png and /dev/null differ
diff --git a/examples/ParulaContours_dark.png b/examples/ParulaContours_dark.png
deleted file mode 100644
index 7e1d2a7..0000000
Binary files a/examples/ParulaContours_dark.png and /dev/null differ
diff --git a/examples/ParulaContours_grey.png b/examples/ParulaContours_grey.png
deleted file mode 100644
index ece2581..0000000
Binary files a/examples/ParulaContours_grey.png and /dev/null differ
diff --git a/examples/ParulaContours_navy.png b/examples/ParulaContours_navy.png
deleted file mode 100644
index e90424c..0000000
Binary files a/examples/ParulaContours_navy.png and /dev/null differ
diff --git a/examples/bar_chart.png b/examples/bar_chart.png
deleted file mode 100644
index 06e2881..0000000
Binary files a/examples/bar_chart.png and /dev/null differ
diff --git a/examples/coloredLine.png b/examples/coloredLine.png
deleted file mode 100644
index 3b9b2ae..0000000
Binary files a/examples/coloredLine.png and /dev/null differ
diff --git a/examples/coloredLine_dark.png b/examples/coloredLine_dark.png
deleted file mode 100644
index 3507b68..0000000
Binary files a/examples/coloredLine_dark.png and /dev/null differ
diff --git a/examples/defaultPulseResponse.png b/examples/defaultPulseResponse.png
deleted file mode 100644
index d6c8fa7..0000000
Binary files a/examples/defaultPulseResponse.png and /dev/null differ
diff --git a/examples/niceplotsPulseResponse.png b/examples/niceplotsPulseResponse.png
deleted file mode 100644
index c350030..0000000
Binary files a/examples/niceplotsPulseResponse.png and /dev/null differ
diff --git a/examples/optProb-hashed.png b/examples/optProb-hashed.png
deleted file mode 100644
index fe8e09f..0000000
Binary files a/examples/optProb-hashed.png and /dev/null differ
diff --git a/examples/optProb-shaded.png b/examples/optProb-shaded.png
deleted file mode 100644
index b6009da..0000000
Binary files a/examples/optProb-shaded.png and /dev/null differ
diff --git a/examples/opt_stacks.png b/examples/opt_stacks.png
deleted file mode 100644
index 62816b3..0000000
Binary files a/examples/opt_stacks.png and /dev/null differ
diff --git a/examples/opt_stacks_more_data.png b/examples/opt_stacks_more_data.png
deleted file mode 100644
index 530c793..0000000
Binary files a/examples/opt_stacks_more_data.png and /dev/null differ
diff --git a/examples/plot_bar_chart.py b/examples/plot_bar_chart.py
index e8faa49..8d5e64d 100644
--- a/examples/plot_bar_chart.py
+++ b/examples/plot_bar_chart.py
@@ -3,41 +3,23 @@
=========
An example of a bar chart.
"""
-import random
import matplotlib.pyplot as plt
import niceplots
-niceplots.setRCParams()
-try:
- # use random words for the example
- word_file = "/usr/share/dict/words"
- words = open(word_file).read().splitlines()
- wl = len(words)
- random.seed(100)
+header = ["Method", "Time (sec)"]
+labels = [
+ "Analytic Forward",
+ "Analytic Adjoint",
+ "FD Forward Diff.",
+ "FD Central Diff.",
+ "FD Backward Diff.",
+]
+times = [0.00456, 0.00847, 0.0110, 0.0213, 0.011]
+nd = 4
- def rw():
- return random.choice(words)
+with plt.style.context(niceplots.get_style()):
+ niceplots.horiz_bar(labels, times, header, nd=nd, size=[7, 0.65])
- header = [rw(), rw()]
- n = 15 # number of bars to create
- labels = [rw() for i in range(n)]
- times = [random.random() * random.randint(0, 1000) for i in range(n)]
- nd = 1
-
-except FileNotFoundError: # noqa: E722 if user is not on a *nix system
- header = ["Method", "Time (sec)"]
- labels = [
- "Analytic Forward",
- "Analytic Adjoint",
- "FD Forward Diff.",
- "FD Central Diff.",
- "FD Backward Diff.",
- ]
- times = [0.00456, 0.00847, 0.0110, 0.0213, 0.011]
- nd = 4
-
-niceplots.horiz_bar(labels, times, header, nd=nd, size=[7, 0.65])
-
-plt.savefig("bar_chart.pdf", bbox_inches="tight")
-plt.savefig("bar_chart.png", dpi=400, bbox_inches="tight")
+ plt.savefig("bar_chart.png")
+ plt.savefig("bar_chart.svg")
diff --git a/examples/plot_color_demo.py b/examples/plot_color_demo.py
new file mode 100644
index 0000000..2ec9090
--- /dev/null
+++ b/examples/plot_color_demo.py
@@ -0,0 +1,66 @@
+"""
+==============================================================================
+NicePlots Style Colors Demo
+==============================================================================
+This script demonstrates colors available in each style.
+"""
+
+# ==============================================================================
+# Standard Python modules
+# ==============================================================================
+
+# ==============================================================================
+# External Python modules
+# ==============================================================================
+import matplotlib.pyplot as plt
+import niceplots
+import numpy as np
+
+# ==============================================================================
+# Extension modules
+# ==============================================================================
+
+# Get the style names from NicePlots
+styles = niceplots.get_available_styles()
+
+plt.style.use(niceplots.get_style("james-light"))
+
+fig, axs = plt.subplots(1, len(styles), figsize=(3 * len(styles), 8))
+axs = axs.flatten()
+
+for i, style in enumerate(styles):
+ with plt.style.context(niceplots.get_style(style)):
+ ax = axs[i]
+ colors = niceplots.get_colors()
+ background = colors["Background"]
+ for key in ["Axis", "Background", "Text", "Label"]:
+ del colors[key]
+
+ ax.set_title(style, pad=20.0)
+
+ # Fill the background with the style's background color
+ ax.fill_between([0, 1], [0, 0], [1, 1], color=background)
+
+ # Swatch properties
+ spacing = 0.05
+ y_spacing = spacing * 3
+ width = (1 - (len(colors) + 1) * spacing) / len(colors)
+
+ # Plot the swatches
+ for i_color, color in enumerate(colors.keys()):
+ y_start = i_color * (spacing + width) + spacing
+ y_end = width + y_start
+ ax.fill_between([y_spacing, 1 - y_spacing], [y_start] * 2, [y_end] * 2, color=colors[color])
+ ax.text(0.5, (y_start + y_end) / 2, color, va="center", ha="center")
+
+ ax.set_xlim([0, 1])
+ ax.set_ylim([0, 1])
+ ax.invert_yaxis()
+
+ # Remove the spines and ticks
+ ax.spines[["top", "bottom", "left", "right"]].set_visible(False)
+ ax.set_xticks(())
+ ax.set_yticks(())
+
+fig.savefig("style_color_demo.png")
+fig.savefig("style_color_demo.svg")
diff --git a/examples/plot_colored_line.py b/examples/plot_colored_line.py
index 705070b..69958a4 100644
--- a/examples/plot_colored_line.py
+++ b/examples/plot_colored_line.py
@@ -1,7 +1,7 @@
"""
Colored line plotting
=====================
-An example of the plotColoredLine function, plotting the sine and cosine functions,
+An example of the plot_colored_line function, plotting the sine and cosine functions,
colored by their derivatives
"""
@@ -21,33 +21,37 @@
# Extension modules
# ==============================================================================
-niceplots.setRCParams()
+plt.style.use(niceplots.get_style())
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)
c = np.cos(x)
fig, ax = plt.subplots()
-niceplots.plotColoredLine(x, y, c, cmap="coolwarm", fig=fig, ax=ax, addColorBar=True, cRange=None, cBarLabel="$dy/dx$")
-niceplots.plotColoredLine(x, c, -y, cmap="coolwarm", fig=fig, ax=ax)
+niceplots.plot_colored_line(
+ x, y, c, cmap="coolwarm", fig=fig, ax=ax, addColorBar=True, cRange=None, cBarLabel="$dy/dx$", clip_on=False
+)
+niceplots.plot_colored_line(x, c, -y, cmap="coolwarm", fig=fig, ax=ax, clip_on=False)
niceplots.adjust_spines(ax)
ax.set_xlabel("$x$")
ax.set_ylabel("$y$", rotation="horizontal", ha="right")
ax.set_xticks(np.linspace(0, 2, 5) * np.pi)
ax.set_xticklabels([0, r"$\frac{\pi}{2}$", r"$\pi$", r"$\frac{3\pi}{2}$", r"$2\pi$"])
ax.set_xlim(0, 2 * np.pi)
-fig.savefig("coloredLine.png", dpi=400)
-fig.savefig("coloredLine.pdf")
+fig.savefig("colored_line.png")
+fig.savefig("colored_line.svg")
# Use a custom norm to specify the colormap range
divnorm = TwoSlopeNorm(vmin=-1.0, vcenter=0.8, vmax=1.0)
-fig, ax = niceplots.plotColoredLine(x, y, c, cmap="coolwarm", norm=divnorm, addColorBar=False, cBarLabel="$dy/dx$")
+fig, ax = niceplots.plot_colored_line(
+ x, y, c, cmap="coolwarm", norm=divnorm, addColorBar=False, cBarLabel="$dy/dx$", clip_on=False
+)
niceplots.adjust_spines(ax)
ax.set_xlabel("$x$")
ax.set_ylabel("$y$", rotation="horizontal", ha="right")
ax.set_xticks(np.linspace(0, 2, 5) * np.pi)
ax.set_xticklabels([0, r"$\frac{\pi}{2}$", r"$\pi$", r"$\frac{3\pi}{2}$", r"$2\pi$"])
ax.set_xlim(0, 2 * np.pi)
-fig.savefig("coloredLineCustomNorm.png", dpi=400)
-fig.savefig("coloredLineCustomNorm.pdf")
-# plt.show()
+
+fig.savefig("colored_line_custom_norm.png")
+fig.savefig("colored_line_custom_norm.svg")
diff --git a/examples/plot_dark_mode.py b/examples/plot_dark_mode.py
deleted file mode 100644
index 75a2305..0000000
--- a/examples/plot_dark_mode.py
+++ /dev/null
@@ -1,107 +0,0 @@
-"""
-==============================================================================
-Dark mode plotting example
-==============================================================================
-An example of dark mode usage applied to some of the existing examples. Shows
-various usage of the dark_mode and set_dark_background in
-:func:`niceplots.utils.setRCParams`.
-"""
-
-# ==============================================================================
-# Standard Python modules
-# ==============================================================================
-
-# ==============================================================================
-# External Python modules
-# ==============================================================================
-import numpy as np
-import matplotlib.pyplot as plt
-import niceplots
-
-# ==============================================================================
-# Extension modules
-# ==============================================================================
-
-# Dark mode with a transparent background, saved to a png
-niceplots.setRCParams(dark_mode=True)
-# The next line sets the thumbnail for the sphinx gallery in docs
-# sphinx_gallery_thumbnail_number = 2
-
-x = np.linspace(0, 2 * np.pi, 100)
-y = np.sin(x)
-c = np.cos(x)
-
-fig, ax = plt.subplots()
-niceplots.plotColoredLine(x, y, c, cmap="coolwarm", fig=fig, ax=ax, addColorBar=True, cRange=None, cBarLabel="$dy/dx$")
-niceplots.plotColoredLine(x, c, -y, cmap="coolwarm", fig=fig, ax=ax, addColorBar=True, cRange=None, cBarLabel="$dy/dx$")
-niceplots.adjust_spines(ax)
-ax.set_xlabel("$x$")
-ax.set_ylabel("$y$", rotation="horizontal", ha="right")
-ax.set_xticks(np.linspace(0, 2, 5) * np.pi)
-ax.set_xticklabels([0, r"$\frac{\pi}{2}$", r"$\pi$", r"$\frac{3\pi}{2}$", r"$2\pi$"])
-ax.set_xlim(0, 2 * np.pi)
-fig.savefig("coloredLine_dark.png", transparent=True, dpi=400)
-
-
-# Plot contours with the default and a custom background color
-niceColors = niceplots.get_niceColors()
-
-bkgnd = [True, niceColors["Grey"], "#0c1c38"] # different ways of setting the background via set_dark_background
-fname_suffix = ["_dark", "_grey", "_navy"]
-
-for i, back in enumerate(bkgnd):
- niceplots.setRCParams(dark_mode=True, set_dark_background=back)
-
- def f(x1, x2):
- return x1**3 + 2.0 * x1 * x2**2 - x2**3 - 20.0 * x1
-
- x1 = x2 = np.linspace(-5, 5, 201)
- X1, X2 = np.meshgrid(x1, x2)
- minimum = [2.58199, 0]
-
- q2fig, q2ax = plt.subplots()
- q2ax.contour(X1, X2, f(X1, X2), cmap=niceplots.parula.parula_map, levels=40)
- q2ax.plot(
- minimum[0],
- minimum[1],
- clip_on=False,
- marker="o",
- color="w",
- markeredgecolor=q2ax.get_facecolor(),
- linestyle="",
- markersize=12,
- )
- q2ax.annotate(
- "Local Minimum",
- xy=minimum,
- xytext=(-5, 10),
- textcoords="offset points",
- va="bottom",
- ha="center",
- )
- q2ax.plot(
- -minimum[0],
- minimum[1],
- clip_on=False,
- marker="o",
- color="w",
- markeredgecolor=q2ax.get_facecolor(),
- linestyle="",
- markersize=12,
- )
- q2ax.annotate(
- "Local Maximum",
- xy=(-minimum[0], 0),
- xytext=(0, -10),
- textcoords="offset points",
- va="top",
- ha="center",
- )
- niceplots.adjust_spines(q2ax, outward=True)
- q2ax.set_xlabel("$x_1$")
- q2ax.set_xticks([min(x1), -minimum[0], 0, minimum[0], max(x1)])
- q2ax.set_xlim(left=min(x1), right=max(x1))
- q2ax.set_ylim(bottom=min(x2), top=max(x2))
- q2ax.set_yticks([min(x2), 0, minimum[-1], max(x2)])
- q2ax.set_ylabel("$x_2$", rotation="horizontal", ha="right")
- plt.savefig(f"ParulaContours{fname_suffix[i]}.png", dpi=400)
diff --git a/examples/plot_default_formatting.py b/examples/plot_default_formatting.py
deleted file mode 100644
index 654dadf..0000000
--- a/examples/plot_default_formatting.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-Default formatting
-==================
-A short example to demonstrate the use of niceplots default plot formatting and compre it to matplotlib defaults
-"""
-
-import numpy as np
-import matplotlib.pyplot as plt
-import niceplots
-
-
-# This function computes the displacement history of an undamped oscilator subjec to to a force pulse of length tp
-def MSPulseResponse(t, tp, omega):
- x = np.where(
- t < tp,
- 1.0 - np.cos(omega * t),
- (np.cos(omega * tp) - 1.0) * np.cos(omega * t) + np.sin(omega * tp) * np.sin(omega * t),
- )
- return x
-
-
-m = 1.0
-k = 100.0
-omega = np.sqrt(k / m)
-t = np.linspace(0, 3.0, 1001)
-TP = [0.5, 0.8, 1.2, 1.6, 2.0]
-
-for formatting in ["default", "niceplots"]:
- if formatting == "niceplots":
- niceplots.setRCParams()
- colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
-
- fig, axes = plt.subplots(nrows=len(TP), figsize=(12, 16))
-
- for i in range(len(TP)):
- tp = TP[i]
- ax = axes[i]
- x = MSPulseResponse(t, tp, omega)
- line = ax.plot(t, x, clip_on=False, color=colors[i])
- ax.vlines(tp, -3, 1.0 - np.cos(omega * tp), linestyle="--", color="gray", zorder=0)
- ax.set_xticks([0, tp, 3])
- if i == len(TP) - 1:
- ax.set_xlabel("t (s)")
- ax.set_ylabel(r"$\frac{x(t)}{x_s}$", ha="right", rotation="horizontal")
- ax.set_ylim(bottom=-2.0, top=2.0)
- if formatting == "niceplots":
- niceplots.adjust_spines(ax, outward=True)
-
- plt.savefig(f"{formatting}PulseResponse.pdf")
- plt.savefig(f"{formatting}PulseResponse.png", dpi=400)
diff --git a/examples/plot_nested_pie_chart.py b/examples/plot_nested_pie_chart.py
index 38ea37c..b93a4ed 100644
--- a/examples/plot_nested_pie_chart.py
+++ b/examples/plot_nested_pie_chart.py
@@ -4,9 +4,10 @@
An example of a nested pie chart.
"""
import matplotlib.pyplot as plt
-from niceplots import setRCParams, plotNestedPie
+import niceplots
-setRCParams()
+plt.style.use(niceplots.get_style("james-dark"))
+colors = niceplots.get_colors()
data = {
"Pie": {
@@ -25,12 +26,8 @@
},
}
-# Custom colors
-colors = ["#e86492", "#f0a43a", "#56b2f0"]
-
-fig, ax = plt.subplots(figsize=(13, 8))
-pieObjects = plotNestedPie(data, colors=colors, ax=ax)
-ax.set_title("The best pies")
+fig, ax = plt.subplots(figsize=(8, 8))
+pieObjects = niceplots.plot_nested_pie(data, colors=list(colors.values()), ax=ax)
# Customize one of the wedges...
pieObjects["Pizza"]["Cheese"]["wedge"].set_radius(1.1)
@@ -40,5 +37,5 @@
pieObjects["Pizza"]["Cheese"]["text"].set_weight("bold")
pieObjects["Pizza"]["Cheese"]["text"].set_x(-0.82)
-plt.savefig("nested_pie_chart.pdf", bbox_inches="tight")
-plt.savefig("nested_pie_chart.png", dpi=400, bbox_inches="tight")
+plt.savefig("nested_pie_chart.png")
+plt.savefig("nested_pie_chart.svg")
diff --git a/examples/plot_optProb.py b/examples/plot_opt_prob.py
similarity index 84%
rename from examples/plot_optProb.py
rename to examples/plot_opt_prob.py
index 0f0ce85..88ab35c 100644
--- a/examples/plot_optProb.py
+++ b/examples/plot_opt_prob.py
@@ -1,7 +1,7 @@
"""
Plotting optimization problem
=============================
-An example of how to use the :func:`niceplots.utils.plotOptProb` function to plot a constrained 2D optimization problem.
+An example of how to use the :func:`niceplots.utils.plot_opt_prob` function to plot a constrained 2D optimization problem.
This example plots the 2D Rosenbrock function with a quadratic equality constraint and 3 circular inequality
constraints, which has an optimum at (1, 1).
"""
@@ -20,8 +20,8 @@
# Extension modules
# ==============================================================================
-niceplots.setRCParams()
-niceColors = niceplots.get_niceColors()
+plt.style.use(niceplots.get_style())
+colors = niceplots.get_colors()
def Rosenbrock(x, y):
@@ -51,7 +51,7 @@ def eqCon(x, y):
for conStyle in ["shaded", "hashed"]:
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_aspect("equal")
- niceplots.plotOptProb(
+ niceplots.plot_opt_prob(
Rosenbrock,
xRange=[0, 1.5],
yRange=[0, 1.5],
@@ -72,7 +72,7 @@ def eqCon(x, y):
optX,
optY,
"-o",
- c=niceColors["Grey"],
+ c=colors["Axis"],
markeredgecolor="w",
linewidth=2.0,
markersize=8,
@@ -80,6 +80,5 @@ def eqCon(x, y):
)
# Save figures
- fig.savefig(f"optProb-{conStyle}.png", dpi=400)
- fig.savefig(f"optProb-{conStyle}.pdf")
-plt.show()
+ fig.savefig(f"opt_prob-{conStyle}.png")
+ fig.savefig(f"opt_prob-{conStyle}.svg")
diff --git a/examples/plot_parula_contours.py b/examples/plot_parula_contours.py
index 0bd9f64..3d9642b 100644
--- a/examples/plot_parula_contours.py
+++ b/examples/plot_parula_contours.py
@@ -16,8 +16,8 @@
import niceplots
-niceplots.setRCParams()
-niceColors = niceplots.get_niceColors()
+plt.style.use(niceplots.get_style())
+colors = niceplots.get_colors()
def f(x1, x2):
@@ -28,32 +28,27 @@ def f(x1, x2):
X1, X2 = np.meshgrid(x1, x2)
minimum = [2.58199, 0]
-q2fig, q2ax = plt.subplots()
+q2fig, q2ax = plt.subplots(figsize=(8, 5))
q2ax.contour(X1, X2, f(X1, X2), cmap=niceplots.parula.parula_map, levels=40)
q2ax.plot(
minimum[0],
minimum[1],
clip_on=False,
marker="o",
- color=niceColors["Grey"],
+ color=colors["Axis"],
markeredgecolor="w",
linestyle="",
markersize=12,
)
q2ax.annotate(
- "Local Minimum",
- xy=minimum,
- xytext=(-5, 10),
- textcoords="offset points",
- va="bottom",
- ha="center",
+ "Local Minimum", xy=minimum, xytext=(-5, 10), textcoords="offset points", va="bottom", ha="center", fontsize="small"
)
q2ax.plot(
-minimum[0],
minimum[1],
clip_on=False,
marker="o",
- color=niceColors["Grey"],
+ color=colors["Axis"],
markeredgecolor="w",
linestyle="",
markersize=12,
@@ -65,7 +60,8 @@ def f(x1, x2):
textcoords="offset points",
va="top",
ha="center",
- color=niceColors["Grey"],
+ color=colors["Axis"],
+ fontsize="small",
)
niceplots.adjust_spines(q2ax, outward=True)
q2ax.set_xlabel("$x_1$")
@@ -74,4 +70,6 @@ def f(x1, x2):
q2ax.set_ylim(bottom=min(x2), top=max(x2))
q2ax.set_yticks([min(x2), 0, minimum[-1], max(x2)])
q2ax.set_ylabel("$x_2$", rotation="horizontal", ha="right")
-plt.savefig("ParulaContours.png", dpi=400)
+
+plt.savefig("parula_contours.png")
+plt.savefig("parula_contours.svg")
diff --git a/examples/plot_stacks.py b/examples/plot_stacks.py
index 3f798a9..0a2d282 100644
--- a/examples/plot_stacks.py
+++ b/examples/plot_stacks.py
@@ -8,7 +8,7 @@
import matplotlib.pyplot as plt
import niceplots
-niceplots.setRCParams()
+plt.style.use(niceplots.get_style())
# Set the random seed to get consistent results.
np.random.seed(314)
@@ -37,8 +37,8 @@
# You can set some plot options here as well, like the pad distance for the
# y-labels from the y-axes if you have long labels.
# This function will save a pdf with the filename given.
-niceplots.stacked_plots("Time (s)", time, data[0], figsize=(10, 6), filename="opt_stacks.pdf")
-plt.savefig("opt_stacks.png", dpi=400)
+f, axarr = niceplots.stacked_plots("Time (s)", time, data[0], figsize=(10, 6), filename="opt_stacks.png")
+f.savefig("opt_stacks.svg")
# stacked_plots can also accept a list of dicts to plot multiple strains
# of data on the same set of plots.
@@ -50,6 +50,6 @@
data,
figsize=(10, 6),
line_scaler=0.5,
- filename="opt_stacks_more_data.pdf",
+ filename="opt_stacks_more_data.png",
)
-plt.savefig("opt_stacks_more_data.png", dpi=400)
+f.savefig("opt_stacks_more_data.svg")
diff --git a/examples/plot_style_demo.py b/examples/plot_style_demo.py
new file mode 100644
index 0000000..193bd81
--- /dev/null
+++ b/examples/plot_style_demo.py
@@ -0,0 +1,94 @@
+"""
+==============================================================================
+NicePlots Style Demo
+==============================================================================
+This script demonstrates the styles available in NicePlots and compares them
+to the default matplotlib style. The script generates a plot of a Gaussian
+distributions with some random "measured" data points and a shaded area
+indicating a +/- 1 std dev region. The plot is loosely based on the plot shown
+on page 147 of Jean Luc Doumont's "Trees, maps, and theorems" book.
+"""
+
+# ==============================================================================
+# Standard Python modules
+# ==============================================================================
+
+# ==============================================================================
+# External Python modules
+# ==============================================================================
+import matplotlib.pyplot as plt
+import niceplots
+import numpy as np
+
+# ==============================================================================
+# Extension modules
+# ==============================================================================
+
+
+def gaussian(x, mu, sig):
+ return 1.0 / (np.sqrt(2.0 * np.pi) * sig) * np.exp(-np.power((x - mu) / sig, 2.0) / 2)
+
+
+np.random.seed(0)
+
+xMin = -3
+xMax = 3
+
+# Generate analytic gaussian distribution
+xLine = np.linspace(xMin, xMax, 1000)
+yLine = gaussian(xLine, 0, 1)
+
+# Generate some fake "measured" data points with some noise
+xRand = np.random.uniform(xMin, xMax, 45)
+yRand = gaussian(xRand, 0, 1) + np.random.normal(0, 0.02, len(xRand))
+yRand[yRand < 0] += 0.05
+
+# Create a version of the plot with each niceplots style and the default matplotlib style
+for formatting in ["default"] + niceplots.get_available_styles():
+ with plt.style.context(niceplots.get_style(formatting)):
+
+ # If using a niceplots style, we can get a nice dictionary of the style's colors
+ if formatting != "default":
+ colours = niceplots.get_colors()
+
+ fig, ax = plt.subplots()
+
+ # Plotting tip #1, only add axis ticks at important values
+ ax.set_yticks([0, 0.4])
+ ax.set_ylim(bottom=0, top=0.4)
+ ax.set_xticks([xMin, -1, 0, 1, xMax])
+
+ # Test out some LaTeX in the axis labels
+ ax.set_xlabel("Some variable, $x$")
+ ax.set_ylabel("$\\mathbb{E}(x,\\mu=0, \\sigma=1)$", rotation="horizontal", ha="right")
+
+ # Plot the analytic and measured data, store the returned objects so we can get their colours later
+ (line,) = plt.plot(xLine, yLine, clip_on=False)
+ (markers,) = plt.plot(xRand, yRand, "o", clip_on=False)
+
+ # Plotting tip #2, instead of a legend, put colored labels directly next to the data
+ ax.annotate("Calculated", xy=(-1.2, 0.2), ha="right", va="bottom", color=line.get_color())
+ ax.annotate("Measured", xy=(0.7, 0.395), ha="left", va="top", color=markers.get_color())
+
+ # Plot the shaded area indicating the +/- 1 std dev region.
+ # If using a niceplots style, we can use the colours dictionary to make the region the same color as the axes,
+ # otherwise we'll just use gray
+ if formatting == "default":
+ fill_color = "gray"
+ else:
+ fill_color = colours["Axis"]
+
+ ax.fill_between(xLine, yLine, 0, where=np.abs(xLine) <= 1, facecolor=fill_color, alpha=0.2, zorder=0)
+ ax.annotate("68.27%", xy=(0, 0.075), ha="center", va="bottom", color=fill_color)
+ plt.annotate("", xy=(-1, 0.07), xytext=(1.0, 0.07), arrowprops=dict(arrowstyle="<|-|>", color=fill_color))
+
+ fig.suptitle(f"{formatting} style")
+
+ # Plotting tip #3, use the niceplots adjust_spines function to space the axes out nicely
+ if formatting != "default":
+ niceplots.adjust_spines(ax)
+ else:
+ plt.tight_layout()
+
+ fig.savefig(f"{formatting}_style_demo.svg")
+ fig.savefig(f"{formatting}_style_demo.png")
diff --git a/examples/ref/bar_chart.png b/examples/ref/bar_chart.png
new file mode 100644
index 0000000..c4d734d
Binary files /dev/null and b/examples/ref/bar_chart.png differ
diff --git a/examples/ref/colored_line.png b/examples/ref/colored_line.png
new file mode 100644
index 0000000..077c0a0
Binary files /dev/null and b/examples/ref/colored_line.png differ
diff --git a/examples/ref/colored_line_custom_norm.png b/examples/ref/colored_line_custom_norm.png
new file mode 100644
index 0000000..1925837
Binary files /dev/null and b/examples/ref/colored_line_custom_norm.png differ
diff --git a/examples/ref/default_style_demo.png b/examples/ref/default_style_demo.png
new file mode 100644
index 0000000..3b19e5d
Binary files /dev/null and b/examples/ref/default_style_demo.png differ
diff --git a/examples/ref/doumont-dark_style_demo.png b/examples/ref/doumont-dark_style_demo.png
new file mode 100644
index 0000000..1302582
Binary files /dev/null and b/examples/ref/doumont-dark_style_demo.png differ
diff --git a/examples/ref/doumont-light_style_demo.png b/examples/ref/doumont-light_style_demo.png
new file mode 100644
index 0000000..7cc85ad
Binary files /dev/null and b/examples/ref/doumont-light_style_demo.png differ
diff --git a/examples/ref/james-dark_style_demo.png b/examples/ref/james-dark_style_demo.png
new file mode 100644
index 0000000..9306f5d
Binary files /dev/null and b/examples/ref/james-dark_style_demo.png differ
diff --git a/examples/ref/james-light_style_demo.png b/examples/ref/james-light_style_demo.png
new file mode 100644
index 0000000..15fbb2b
Binary files /dev/null and b/examples/ref/james-light_style_demo.png differ
diff --git a/examples/ref/nested_pie_chart.png b/examples/ref/nested_pie_chart.png
new file mode 100644
index 0000000..3a6459b
Binary files /dev/null and b/examples/ref/nested_pie_chart.png differ
diff --git a/examples/ref/opt_prob-hashed.png b/examples/ref/opt_prob-hashed.png
new file mode 100644
index 0000000..f6dc277
Binary files /dev/null and b/examples/ref/opt_prob-hashed.png differ
diff --git a/examples/ref/opt_prob-shaded.png b/examples/ref/opt_prob-shaded.png
new file mode 100644
index 0000000..b5ce355
Binary files /dev/null and b/examples/ref/opt_prob-shaded.png differ
diff --git a/examples/ref/opt_stacks.png b/examples/ref/opt_stacks.png
new file mode 100644
index 0000000..8f65a32
Binary files /dev/null and b/examples/ref/opt_stacks.png differ
diff --git a/examples/ref/opt_stacks_more_data.png b/examples/ref/opt_stacks_more_data.png
new file mode 100644
index 0000000..d5853db
Binary files /dev/null and b/examples/ref/opt_stacks_more_data.png differ
diff --git a/examples/ref/parula_contours.png b/examples/ref/parula_contours.png
new file mode 100644
index 0000000..20af10c
Binary files /dev/null and b/examples/ref/parula_contours.png differ
diff --git a/examples/ref/style_color_demo.png b/examples/ref/style_color_demo.png
new file mode 100644
index 0000000..b011458
Binary files /dev/null and b/examples/ref/style_color_demo.png differ
diff --git a/niceplots/__init__.py b/niceplots/__init__.py
index 710a419..818f157 100644
--- a/niceplots/__init__.py
+++ b/niceplots/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "1.3.0"
+__version__ = "2.0.0"
from .utils import *
from .parula import *
diff --git a/niceplots/styles/README.md b/niceplots/styles/README.md
new file mode 100644
index 0000000..8fe0124
--- /dev/null
+++ b/niceplots/styles/README.md
@@ -0,0 +1,15 @@
+New styles can be added here by making a new file.
+
+### File naming
+
+The filename must end in `.mplstyle`.
+Whatever is before `.mplstyle` will be the string used to reference the style when users call `setStyle` or `styleContext`.
+
+### File contents
+
+There are only two NicePlots-specific stylesheet requirements:
+
+1. The color cycle is specified using the `axes.prop_cycle` rcParam.
+2. The name of each color in the color cycle is specified in the `keymap.help` rcParam.
+
+The existing stylesheets can be used as a model.
diff --git a/niceplots/styles/doumont-dark.mplstyle b/niceplots/styles/doumont-dark.mplstyle
new file mode 100644
index 0000000..f49c34d
--- /dev/null
+++ b/niceplots/styles/doumont-dark.mplstyle
@@ -0,0 +1,55 @@
+# Figure settings
+savefig.dpi : 600
+figure.constrained_layout.use : True
+
+
+# Axes settings
+axes.spines.top : False
+axes.spines.right : False
+axes.labelpad : 8.0
+axes.xmargin : 0
+axes.ymargin : 0
+
+
+# Font settings
+font.family : sans-serif
+font.sans-serif : CMU Bright
+axes.unicode_minus : False
+font.size : 18
+
+axes.labelweight : bold
+
+xtick.labelsize : small
+ytick.labelsize : small
+
+text.latex.preamble : r"\usepackage{cmbright}"
+mathtext.default: regular
+pgf.rcfonts : True
+
+
+# Legend settings
+legend.columnspacing : 0.2
+legend.frameon : False
+
+
+# Line settings
+lines.linewidth : 2.0
+lines.markeredgewidth : 1.0
+
+
+# Color settings
+axes.prop_cycle : cycler('color', ['e29400ff', '1E90FF', 'E21A1A', '00a650ff', '800000ff', 'ff8f00', '800080ff', '00A6D6', 'ffffffff'])
+
+# Use this parameter, which (hopefully) nobody will ever use,
+# to store text the names of the colors in the color cycle
+keymap.help : Yellow, Blue, Red, Green, Maroon, Orange, Purple, Cyan, White
+
+
+lines.markeredgecolor : k
+axes.edgecolor : ebebeb
+text.color : ebebeb
+axes.labelcolor : ebebeb
+xtick.color : ebebeb
+ytick.color : ebebeb
+axes.facecolor : k
+figure.facecolor : k
diff --git a/niceplots/styles/doumont-light.mplstyle b/niceplots/styles/doumont-light.mplstyle
new file mode 100644
index 0000000..34daa08
--- /dev/null
+++ b/niceplots/styles/doumont-light.mplstyle
@@ -0,0 +1,50 @@
+# Figure settings
+savefig.dpi : 600
+figure.constrained_layout.use : True
+
+
+# Axes settings
+axes.spines.top : False
+axes.spines.right : False
+axes.labelpad : 8.0
+axes.xmargin : 0
+axes.ymargin : 0
+
+
+# Font settings
+font.family : sans-serif
+font.sans-serif : CMU Bright
+axes.unicode_minus : False
+font.size : 18
+
+axes.labelweight : bold
+
+xtick.labelsize : small
+ytick.labelsize : small
+
+text.latex.preamble : r"\usepackage{cmbright}"
+mathtext.default: regular
+pgf.rcfonts : True
+
+
+# Legend settings
+legend.columnspacing : 0.2
+legend.frameon : False
+
+
+# Line settings
+lines.linewidth : 2.0
+lines.markeredgewidth : 1.0
+
+# Color settings
+axes.prop_cycle : cycler('color', ['e29400ff', '1E90FF', 'E21A1A', '00a650ff', '800000ff', 'ff8f00', '800080ff', '00A6D6', '000000ff'])
+
+# Use this parameter, which (hopefully) nobody will ever use, to store text the names of the colors in the color cycle
+keymap.help : Yellow, Blue, Red, Green, Maroon, Orange, Purple, Cyan, Black
+
+lines.markeredgecolor : w
+axes.edgecolor : 5a5758ff
+text.color : 5a5758ff
+axes.labelcolor : 5a5758ff
+xtick.color : 5a5758ff
+ytick.color : 5a5758ff
diff --git a/niceplots/styles/james-dark.mplstyle b/niceplots/styles/james-dark.mplstyle
new file mode 100644
index 0000000..c907a88
--- /dev/null
+++ b/niceplots/styles/james-dark.mplstyle
@@ -0,0 +1,35 @@
+font.family : sans-serif
+font.sans-serif : Prompt
+font.weight : 300
+axes.unicode_minus : True
+font.size : 16
+figure.dpi : 100
+savefig.dpi : 600
+axes.spines.top : False
+axes.spines.right : False
+mathtext.default: regular
+
+legend.columnspacing : 0.2
+legend.frameon : False
+figure.constrained_layout.use : True
+
+lines.linewidth : 2.0
+
+axes.prop_cycle : cycler('color', ['52a1fa', 'f26f6f', 'd6d6d6', 'c678fa', '78b865', 'e6ad53'])
+
+# Use this parameter, which (hopefully) nobody will ever use,
+# to store text the names of the colors in the color cycle
+keymap.help : Blue, Red, White, Purple, Green, Orange
+
+axes.edgecolor : bfd0e3
+text.color : f5f5f5
+axes.labelcolor : f5f5f5
+axes.labelweight : 500
+xtick.color : bfd0e3
+ytick.color : bfd0e3
+xtick.labelsize : small
+ytick.labelsize : small
+axes.facecolor : 011d3b
+figure.facecolor : 011d3b
+
+pgf.rcfonts : True
diff --git a/niceplots/styles/james-light.mplstyle b/niceplots/styles/james-light.mplstyle
new file mode 100644
index 0000000..f1ba0db
--- /dev/null
+++ b/niceplots/styles/james-light.mplstyle
@@ -0,0 +1,35 @@
+font.family : sans-serif
+font.sans-serif : Prompt
+font.weight : 300
+axes.unicode_minus : True
+font.size : 16
+figure.dpi : 100
+savefig.dpi : 600
+axes.spines.top : False
+axes.spines.right : False
+mathtext.default: regular
+
+legend.columnspacing : 0.2
+legend.frameon : False
+figure.constrained_layout.use : True
+
+lines.linewidth : 2.0
+
+axes.prop_cycle : cycler('color', ['52a1fa', 'f26f6f', '485263', 'ae66de', '3eb051', 'faaa48'])
+
+# Use this parameter, which (hopefully) nobody will ever use,
+# to store text the names of the colors in the color cycle
+keymap.help : Blue, Red, Navy, Purple, Green, Orange
+
+axes.edgecolor : 3b4c66
+text.color : 273345
+axes.labelcolor : 273345
+axes.labelweight : 500
+xtick.color : 3b4c66
+ytick.color : 3b4c66
+xtick.labelsize : small
+ytick.labelsize : small
+axes.facecolor : none
+figure.facecolor : none
+
+pgf.rcfonts : True
diff --git a/niceplots/utils.py b/niceplots/utils.py
index 6cb449a..17569d2 100644
--- a/niceplots/utils.py
+++ b/niceplots/utils.py
@@ -1,127 +1,124 @@
import matplotlib.pyplot as plt
import numpy as np
-from cycler import cycler
from collections import OrderedDict
from .parula import parula_map
from matplotlib import patheffects
from matplotlib.collections import LineCollection
import matplotlib.colors as mcolor
import warnings
+import os
-def setRCParams(dark_mode=False, set_dark_background=False):
+def get_style(styleName="doumont-light"):
"""
- Set some defaults for generating nice, Doumont-esque plots.
+ Get the stylesheet to pass to matplotlib's style setting functions. This function
+ works both with niceplots styles and matplotlib's built-in styles. Usage examples::
+
+ import matplotlib.pyplot as plt
+ import niceplots
+
+ plt.style.use(niceplots.get_style())
+ plt.plot([0, 1], [0, 1])
+
+ # Or you can use it within a context manager
+ with plt.style.context(niceplots.get_style()):
+ plt.plot([0, 1], [0, 1])
+
+ # Also try different styles
+ plt.style.use(niceplots.get_style("james-dark")) # niceplots james dark style
+ plt.style.use(niceplots.get_style("default")) # matplotlib default style
Parameters
----------
- dark_mode (optional) : bool
- If true, sets axes, labels, etc. to white so the plot
- can be used on a dark background.
- NOTE: Unless you are explicitly saving the plot with
- a transparent background (e.g. as a png with
- transparent=True), also set the
- set_dark_background option.
- set_dark_background (optional) : bool or str
- If true, sets the axis and figure backgrounds to black.
- This option can also be set to a color string, such as
- "#aaaaaa", to use a color other than black.
+ styleName : str, optional
+ Name of desired style. By default uses doumont-light style. Avaiable styles are:
+
+ - doumont-light: the niceplots style you know and love
+ - doumont-dark: the dark version of the niceplots style you know and love
+ - james-dark: a really cool alternative to classic niceplots
+ - james-light: a version of james with a light background, naturally
+
+ Returns
+ -------
+ str
+ The style string to be passed to one of matplotlib's style setting functions.
"""
- plt.rcParams["font.family"] = "sans-serif"
- plt.rcParams["font.sans-serif"] = ["CMU Bright"]
- plt.rcParams["axes.unicode_minus"] = False
- plt.rcParams["font.size"] = 24
- plt.rcParams["figure.dpi"] = 100
- plt.rcParams["figure.figsize"] = [12, 6.75]
- plt.rcParams["savefig.dpi"] = 600
- plt.rcParams["axes.spines.top"] = False
- plt.rcParams["axes.spines.right"] = False
- plt.rcParams["axes.labelpad"] = 8.0
- plt.rcParams["text.latex.preamble"] = r"\usepackage{cmbright}"
-
- plt.rcParams["legend.columnspacing"] = 0.2
- plt.rcParams["legend.frameon"] = False
- plt.rcParams["figure.constrained_layout.use"] = True
-
- plt.rcParams["patch.edgecolor"] = "w"
-
- plt.rcParams["axes.spines.top"] = False
- plt.rcParams["axes.spines.right"] = False
-
- plt.rcParams["axes.autolimit_mode"] = "round_numbers"
- plt.rcParams["axes.xmargin"] = 0
- plt.rcParams["axes.ymargin"] = 0
-
- plt.rcParams["lines.linewidth"] = 2.0
- plt.rcParams["lines.markeredgewidth"] = 1.0
- plt.rcParams["lines.markeredgecolor"] = "w"
-
- niceColors = get_niceColors()
- plt.rcParams["axes.prop_cycle"] = cycler("color", list(niceColors.values())[:-2])
-
- # Color for axes, labels, ticks, text, etc.
- color = niceColors["Grey"]
- if dark_mode:
- plt.rcParams["patch.edgecolor"] = "#000000"
- color = "#ffffff" # white
-
- plt.rcParams["axes.edgecolor"] = color
- plt.rcParams["text.color"] = color
- plt.rcParams["axes.labelcolor"] = color
- plt.rcParams["axes.labelweight"] = 200
- plt.rcParams["xtick.color"] = color
- plt.rcParams["ytick.color"] = color
-
- # Set the axis and figure background color if necessary
- if isinstance(set_dark_background, bool):
- if set_dark_background:
- # Set background to black
- plt.rcParams["axes.facecolor"] = "#000000"
- plt.rcParams["figure.facecolor"] = "#000000"
- elif isinstance(set_dark_background, str):
- # Set background to set_dark_background
- plt.rcParams["axes.facecolor"] = set_dark_background
- plt.rcParams["figure.facecolor"] = set_dark_background
- plt.rcParams["patch.edgecolor"] = set_dark_background
- else:
- raise TypeError(f"set_dark_background value of {set_dark_background} is invalid")
-
-
-def get_niceColors():
- # Define an ordered dictionary of some nice Doumont style colors to use as the default color cycle
- niceColors = OrderedDict()
- niceColors["Yellow"] = "#e29400ff" # '#f8a30dff'
- niceColors["Blue"] = "#1E90FF"
- niceColors["Red"] = "#E21A1A"
- niceColors["Green"] = "#00a650ff"
- niceColors["Maroon"] = "#800000ff"
- niceColors["Orange"] = "#ff8f00"
- niceColors["Purple"] = "#800080ff"
- niceColors["Cyan"] = "#00A6D6"
- niceColors["Black"] = "#000000ff"
- # The 2 colours below are not used in the colour cycle as they are too close to the other colours.
- # Grey is kept in the dictionary as it is used as the default axis/tick colour.
- # RedOrange is the old Orange, and is kept because I think it looks nice.
- niceColors["Grey"] = "#5a5758ff"
- niceColors["RedOrange"] = "#E21A1A"
-
- return niceColors
-
-
-def get_delftColors():
- # Define an ordered dictionary of the official TU Delft colors to use as the default color cycle
- delftColors = OrderedDict()
- delftColors["Cyan"] = "#00A6D6" # '#f8a30dff'
- delftColors["Yellow"] = "#E1C400"
- delftColors["Purple"] = "#6D177F"
- delftColors["Red"] = "#E21A1A"
- delftColors["Green"] = "#A5CA1A"
- delftColors["Blue"] = "#1D1C73"
- delftColors["Orange"] = "#E64616"
- delftColors["Grey"] = "#5a5758ff"
- delftColors["Black"] = "#000000ff"
-
- return delftColors
+ # If the style is a niceplots style, return the file path
+ if styleName in get_available_styles():
+ curDir = os.path.dirname(os.path.abspath(__file__))
+ return os.path.join(curDir, "styles", styleName + ".mplstyle")
+
+ # Otherwise assume it's a matplotlib style and just return the style name
+ return styleName
+
+
+def get_colors():
+ """
+ Get a dictionary with the colors for the current style. This function
+ only works when niceplots styles are used (not built-in matplotlib ones).
+
+ Returns
+ -------
+ dict
+ Dictionary of the colors for the requested style. The keys
+ are human-readable names and the keys are the hex codes. It
+ also adds color names from the rcParams that are generally useful:
+
+ - "Axis": axis spine color
+ - "Background" axis background color
+ - "Text": default text color
+ - "Label": axis label color
+ """
+ # Get the color codes and their names from the (hopefully) "special" parameter
+ color_codes = get_colors_list()
+ color_names = plt.rcParams["keymap.help"]
+
+ # Ensure that the amount of color names matches the amount of colors
+ if len(color_codes) != len(color_names):
+ raise ValueError(
+ "The colors are not properly named in the stylesheet, please open an issue on GitHub with the details!"
+ )
+
+ colors = OrderedDict(zip(color_names, color_codes))
+ colors["Axis"] = plt.rcParams["axes.edgecolor"]
+ colors["Background"] = plt.rcParams["axes.facecolor"]
+ colors["Text"] = plt.rcParams["text.color"]
+ colors["Label"] = plt.rcParams["axes.labelcolor"]
+
+ return colors
+
+
+def get_colors_list():
+ """
+ Get a list with the colors for the current style. This function
+ works with all matplotlib styles.
+
+ Returns
+ -------
+ list
+ List of the colors for the requested style.
+ """
+ return plt.rcParams["axes.prop_cycle"].by_key()["color"]
+
+
+def get_available_styles():
+ """
+ Get a list of the names of styles available.
+
+ Returns
+ -------
+ list
+ The names of the available styles.
+ """
+ curDir = os.path.dirname(os.path.abspath(__file__))
+ styleFilenames = os.listdir(os.path.join(curDir, "styles"))
+ styles = []
+ for s in styleFilenames:
+ name, ext = os.path.splitext(s)
+ if ext == ".mplstyle":
+ styles.append(name)
+ styles.sort() # alphabetize
+ return styles
def handle_close(evt):
@@ -227,10 +224,11 @@ def horiz_bar(labels, times, header, nd=1, size=[5, 0.5], color=None):
Axes on which data is plotted
"""
- # Use niceColours yellow if no colour specified
- niceColours = get_niceColors()
+ # Use the first color if none is specified
if color is None:
- color = niceColours["Yellow"]
+ color = get_colors_list()[0]
+ style_colors = get_colors()
+ line_color = style_colors["Axis"]
# Obtain parameters to size the chart correctly
num = len(times)
@@ -252,7 +250,7 @@ def horiz_bar(labels, times, header, nd=1, size=[5, 0.5], color=None):
for j, (l, t, ax) in enumerate(zip(labels, times, axarr)):
# Draw the gray line and singular yellow dot
- ax.axhline(y=1, c=niceColours["Grey"], lw=3, zorder=0, alpha=0.5)
+ ax.axhline(y=1, c=line_color, lw=3, zorder=0, alpha=0.5)
ax.scatter([t], [1], c=color, lw=0, s=100, zorder=1, clip_on=False)
# Set chart properties
@@ -308,7 +306,7 @@ def stacked_plots(
data_dict_list = [data_dict_list]
if colors is None:
- colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
+ colors = get_colors_list()
data_dict = data_dict_list[0]
n = len(data_dict)
@@ -377,7 +375,7 @@ def stacked_plots(
return f, axarr
-def plotOptProb(
+def plot_opt_prob(
obj,
xRange,
yRange,
@@ -471,7 +469,7 @@ def plotOptProb(
cmap = parula_map
if colors is None:
- colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
+ colors = get_colors_list()
nColor = len(colors)
# --- Create grid of points for evaluating functions ---
@@ -533,7 +531,7 @@ def plotOptProb(
return
-def plotColoredLine(
+def plot_colored_line(
x, y, c, cmap=None, fig=None, ax=None, addColorBar=False, cRange=None, cBarLabel=None, norm=None, **kwargs
):
"""Plot an XY line whose color is determined by some other variable C
@@ -611,7 +609,7 @@ def plotColoredLine(
return
-def plotNestedPie(
+def plot_nested_pie(
data,
colors=None,
alphas=None,
@@ -641,7 +639,8 @@ def plotNestedPie(
colors : str or list of str with hex colors, optional
Colors to use for the inner wedges. Can either specify a qualitative matplotlib colormap (it will assume
this is the case if a string is specified), or a list of colors specified with hex codes (e.g., "#F4A103"),
- by default will use nice colors (niceplots default)
+ by default will use nice colors (niceplots default). Loops through the colors if more categories than
+ colors are specified.
alphas : iterable of floats at least as long as the max number of subcategories for a given category
Transparencies to use to vary the color in the outer categories
ax : matplotlib axes object, optional
@@ -682,12 +681,13 @@ def plotNestedPie(
ax : matplotlib axes object
Axis with the colored line. Returned only if no input ax object is specified
"""
- # If colors is not specified, turn the niceColors into a list of hex colors
+ # If colors is not specified, turn the style's colors into a list of hex colors
if colors is None:
- colors = [c for c in get_niceColors().values()]
+ colors = [c for c in get_colors().values()]
# If colors is given as a qualitative matplotlib colormap, turn it into a list of hex colors
elif isinstance(colors, str):
colors = [mcolor.rgb2hex(plt.colormaps[colors](i)) for i in range(len(data))]
+ numColors = len(colors)
# Go through the colors and only take the color information (not transparency)
for i in range(len(colors)):
@@ -720,13 +720,13 @@ def plotNestedPie(
if alphas is None:
alphas = np.linspace(0.75, 0.95, maxSubcat)[-1::-1]
- innerColors = [colors[i] for i in range(len(data))]
+ innerColors = [colors[i % numColors] for i in range(len(data))]
outerColors = []
iCat = 0
for catVals in data.values():
numSubcats = len(catVals)
for iSubcat in range(numSubcats):
- outerColors.append(colors[iCat] + float.hex(alphas[iSubcat])[4:6])
+ outerColors.append(colors[iCat % numColors] + float.hex(alphas[iSubcat])[4:6])
iCat += 1
# Nested plot fitting params
diff --git a/setup.py b/setup.py
index aca5e6b..8242fca 100644
--- a/setup.py
+++ b/setup.py
@@ -29,4 +29,6 @@
"numpy>=1.16",
"matplotlib>=2.2",
],
+ include_package_data=True,
+ package_data={"": ["styles/*.mplstyle"]},
)