diff --git a/MANIFEST.in b/MANIFEST.in
index c652f2e..fa69952 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,5 @@
include README.md
include LICENSE
-include examples/example.py
\ No newline at end of file
+include examples/example.py
+include plottools/cs_blank.svg
+include plottools/cm/cm_blank.svg
\ No newline at end of file
diff --git a/README.rst b/README.rst
index 59979cd..ec305fb 100644
--- a/README.rst
+++ b/README.rst
@@ -2,3 +2,15 @@ plottools
=========
A package with usefull tools for plotting with matplotlib
+
+
+Installation
+------------
+.. code-block::
+
+ pip install plottools
+
+
+Documentation
+-------------
+Some documentation is available on `pythonhosted `_.
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000..4375c93
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,2 @@
+build
+source/generated
\ No newline at end of file
diff --git a/doc/source/cm.rst b/doc/source/cm.rst
new file mode 100644
index 0000000..5ce94c9
--- /dev/null
+++ b/doc/source/cm.rst
@@ -0,0 +1,7 @@
+cm
+==
+.. toctree::
+ :maxdepth: 3
+
+Some additional color maps
+
\ No newline at end of file
diff --git a/doc/source/conf.py b/doc/source/conf.py
new file mode 100644
index 0000000..87f66b1
--- /dev/null
+++ b/doc/source/conf.py
@@ -0,0 +1,368 @@
+# -*- coding: utf-8 -*-
+#
+# Someproject documentation build configuration file, created by
+# sphinx-quickstart on Mon Sep 05 08:56:23 2016.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+
+import sys
+import os
+
+sys.path.insert(0, os.path.abspath('../..'))
+sys.path.append(os.path.abspath('sphinxext'))
+
+# import the version string
+from plottools.__version__ import version as __version__
+
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.mathjax',
+ 'sphinx.ext.viewcode',
+ 'sphinx.ext.autosummary',
+ 'matplotlib.sphinxext.plot_directive',
+ 'numpydoc',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The encoding of source files.
+#
+# source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'plottools'
+copyright = u'2016, Brecht Baeten'
+author = u'Brecht Baeten'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = unicode(__version__,'utf-8')
+# The full version, including alpha/beta/rc tags.
+release = unicode(__version__,'utf-8')
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#
+# today = ''
+#
+# Else, today_fmt is used as the format for a strftime call.
+#
+# today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#
+# 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
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'alabaster'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+# html_theme_path = []
+
+# The name for this set of Sphinx documents.
+# " v documentation" by default.
+#
+# html_title = u'Someproject v3.1.2'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#
+# html_logo = None
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#
+# html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#
+# html_extra_path = []
+
+# If not None, a 'Last updated on:' timestamp is inserted at every page
+# bottom, using the given strftime format.
+# The empty string is equivalent to '%b %d, %Y'.
+#
+# html_last_updated_fmt = None
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#
+html_sidebars = {
+ '**': [
+ 'about.html',
+ 'navigation.html',
+ 'relations.html',
+ 'searchbox.html',
+ 'donate.html'
+ ]
+}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+#
+# html_domain_indices = True
+
+# If false, no index is generated.
+#
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#
+# html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#
+# html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#
+# html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
+#
+# html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# 'ja' uses this config value.
+# 'zh' user can custom change `jieba` dictionary path.
+#
+# html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#
+# html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Someprojectdoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'plottools.tex', u'plottools Documentation',
+ u'Brecht Baeten', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+#
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#
+# latex_appendices = []
+
+# It false, will not define \strong, \code, itleref, \crossref ... but only
+# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
+# packages.
+#
+# latex_keep_old_macro_names = True
+
+# If false, no module index is generated.
+#
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'plottools', u'plottools Documentation',
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'plottools', u'plottools Documentation',
+ author, 'plottools', 'some descr',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+#
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#
+# texinfo_no_detailmenu = False
+
+autosummary_generate = True
+
+# -- Options for plot directive -------------------------------------------
+plot_include_source = True
\ No newline at end of file
diff --git a/doc/source/cs.rst b/doc/source/cs.rst
new file mode 100644
index 0000000..987f784
--- /dev/null
+++ b/doc/source/cs.rst
@@ -0,0 +1,32 @@
+cs
+==
+.. toctree::
+ :maxdepth: 3
+
+colorscheme
+-----------
+
+.. currentmodule:: plottools.cs.colorscheme
+
+.. autosummary::
+ :nosignatures:
+ :toctree: generated
+
+ Colorscheme
+ ColorschemeTool
+
+
+util
+----
+
+.. currentmodule:: plottools.cs.util
+
+.. autosummary::
+ :nosignatures:
+ :toctree: generated
+
+ plot_colors
+ to_greyscale
+ change_lightness_to_match_greyscale
+ prepare_print_scan
+ analyse_print_scan
diff --git a/doc/source/index.rst b/doc/source/index.rst
new file mode 100644
index 0000000..a8218f8
--- /dev/null
+++ b/doc/source/index.rst
@@ -0,0 +1,29 @@
+.. plottools documentation master file, created by python-git-package.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+plottools
+=========
+
+:Release: |version|
+:Date: |today|
+
+A package with usefull tools for plotting with matplotlib
+
+
+.. toctree::
+ :maxdepth: 3
+
+ installation
+ quickstart
+ reference
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
new file mode 100644
index 0000000..555299b
--- /dev/null
+++ b/doc/source/installation.rst
@@ -0,0 +1,10 @@
+Installation
+============
+.. toctree::
+ :maxdepth: 3
+
+Install using pip
+
+.. code-block:: bash
+
+ pip install plottools
\ No newline at end of file
diff --git a/doc/source/plottools.rst b/doc/source/plottools.rst
new file mode 100644
index 0000000..adaf18c
--- /dev/null
+++ b/doc/source/plottools.rst
@@ -0,0 +1,17 @@
+plottools
+=========
+.. toctree::
+ :maxdepth: 3
+
+.. currentmodule:: plottools
+
+
+.. autosummary::
+ :nosignatures:
+ :toctree: generated
+
+ categorized_xticklabels
+ zoom_axes
+ set_publication_rc
+
+
\ No newline at end of file
diff --git a/doc/source/quickstart.rst b/doc/source/quickstart.rst
new file mode 100644
index 0000000..7084a9a
--- /dev/null
+++ b/doc/source/quickstart.rst
@@ -0,0 +1,106 @@
+Quickstart
+==========
+.. toctree::
+ :maxdepth: 3
+
+Tools
+-----
+
+
+Colorscheme
+-----------
+Plottools offers a colorscheme class which can be used to define a set of colors
+and an order of appearance. The colorscheme object can then be used to cycle
+through colors using a convinient ``next`` method or by indexing using a short
+or longname.
+
+When a colorscheme is defined in the beginning of a script and referenced in the
+subsequent plot commands, it allows easy changes of colorscheme if required.
+
+.. plot::
+
+ import numpy as np
+ import matplotlib.pyplot as plt
+ import plottools as pt
+
+ colorscheme = pt.color
+
+ plt.figure()
+ x = np.arange(6)
+ labels = ['label {}'.format(i+1) for i in range(len(colorscheme.keys()))]
+
+ bars = []
+ y_tot = np.zeros_like(x)
+ for l in labels:
+ y = np.random.random_integers(1,high=10,size=x.shape)
+ b = plt.bar(x, y, 0.8, bottom=y_tot , color=colorscheme.next())
+ bars.append(b[0])
+ y_tot += y
+
+ plt.legend(bars,labels)
+ plt.show()
+
+
+Plottools comes with a default colorscheme ``plottools.color`` which
+includes 6 colors and a shade of grey which looks better than the default colors
+and have good contrast when printed in greyscale.
+
+.. plot::
+
+ import matplotlib.pyplot as plt
+ import numpy as np
+
+ import plottools as pt
+ from plottools.color.util import plot_colors, to_greyscale
+
+ fig = plt.figure()
+ ax1 = fig.add_subplot(211)
+ plot_colors(ax1,pt.color.colors,['k','p','b','r','g','o','y'])
+
+ ax2 = fig.add_subplot(212)
+ plot_colors(ax2,{key: to_greyscale(val) for key,val in pt.color.items()} ,['k','p','b','r','g','o','y'])
+ plt.show()
+
+
+A colorscheme can also be used as the default color cycler by calling the method
+``set_as_default()``.
+
+.. plot::
+
+ import numpy as np
+ import matplotlib.pyplot as plt
+ import plottools as pt
+
+ plt.figure()
+ x = np.linspace(0,2*np.pi,100)
+
+ pt.color.set_as_default()
+
+ for i,key in enumerate( pt.color.keys() ):
+ plt.plot(x,np.sin(x-i*2*2*np.pi/14))
+
+ plt.show()
+
+When a colorscheme is defined, a lighter and darker variation are automatically
+created. These can be accessed as a color scheme object by the ``light`` and
+``dark`` attributes respectively.
+
+.. plot::
+
+ import numpy as np
+ import matplotlib.pyplot as plt
+ import plottools as pt
+
+ plt.figure()
+ x = np.linspace(0,2*np.pi,100)
+ for i,key in enumerate( pt.color.default.keys() ):
+ plt.plot(x,np.sin(x-i*2*2*np.pi/14),color=pt.color[key],label=key)
+ plt.plot(x,np.sin(x-i*2*2*np.pi/14)-0.1,color=pt.color.light[key])
+ plt.plot(x,np.sin(x-i*2*2*np.pi/14)+0.1,color=pt.color.dark[key])
+
+ plt.legend()
+ plt.show()
+
+
+Styles
+------
\ No newline at end of file
diff --git a/doc/source/reference.rst b/doc/source/reference.rst
new file mode 100644
index 0000000..b047faf
--- /dev/null
+++ b/doc/source/reference.rst
@@ -0,0 +1,9 @@
+Reference
+=========
+.. toctree::
+ :maxdepth: 3
+
+ plottools
+ style
+ cs
+ cm
\ No newline at end of file
diff --git a/doc/source/style.rst b/doc/source/style.rst
new file mode 100644
index 0000000..6640bb9
--- /dev/null
+++ b/doc/source/style.rst
@@ -0,0 +1,14 @@
+style
+=====
+.. toctree::
+ :maxdepth: 3
+
+.. currentmodule:: plottools.style
+
+.. autosummary::
+ :nosignatures:
+ :toctree: generated
+
+ horizontalgrid
+ noxticks
+ set
\ No newline at end of file
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 0000000..f08278d
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1 @@
+*.pdf
\ No newline at end of file
diff --git a/examples/categorized_xticklabels.py b/examples/categorized_xticklabels.py
new file mode 100644
index 0000000..481310c
--- /dev/null
+++ b/examples/categorized_xticklabels.py
@@ -0,0 +1,38 @@
+import matplotlib.pyplot as plt
+import numpy as np
+import plottools as pt
+
+
+pt.set_publication_rc()
+
+
+# generate data
+C,B,A = np.meshgrid([10,20],[0.4,0.6,0.8],[1,2,3],indexing='ij')
+xticklabels = [ A.reshape((-1,)), B.reshape((-1,)), C.reshape((-1,)) ]
+values = [np.random.random(len(t)) for t in xticklabels]
+xticks = np.arange(len(values[0]))
+
+xticklabelnames = ['coord','$B_\mathrm{value}$','CCC']
+labels = ['set1','set2','set3']
+fmt = ['${:.2f}$','${}$ m','$10^{{{:.0f}}}$']
+rotation = [70,0,0]
+
+
+# create the figure
+plt.figure()
+bottom = np.zeros_like(values[0])
+for val,lab in zip(values,labels):
+ plt.bar(xticks+0.05,val,0.9,bottom=bottom,label=lab,color=pt.color.next())
+ bottom += val
+
+plt.legend(framealpha=0.7,loc='upper right')
+plt.ylabel('y-label')
+
+# set the figure style
+pt.style.set(['horizontalgrid','noxticks'])
+
+# add categories on the x-axis
+pt.categorized_xticklabels(xticks+0.5,xticklabels,xticklabelnames=xticklabelnames,fmt=fmt,rotation=rotation)
+plt.tight_layout()
+plt.savefig('categorized_xticklabels.pdf')
+plt.show()
\ No newline at end of file
diff --git a/examples/cm2svg.py b/examples/cm2svg.py
deleted file mode 100644
index 674599d..0000000
--- a/examples/cm2svg.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import matplotlib.pyplot as plt
-import numpy as np
-import plottools as pt
-
-
-# viridis
-pt.cm.cm2svg(plt.cm.viridis,'viridis.svg')
-
-# hot water
-pt.cm.cm2svg(pt.cm.hotwater,'hotwater.svg',stops=5)
-
diff --git a/examples/cm_to_svg.py b/examples/cm_to_svg.py
new file mode 100644
index 0000000..479b2e4
--- /dev/null
+++ b/examples/cm_to_svg.py
@@ -0,0 +1,11 @@
+import matplotlib.pyplot as plt
+import numpy as np
+import plottools as pt
+
+
+# viridis
+pt.cm.cm_to_svg(plt.cm.viridis,'viridis.svg')
+
+# hot water
+pt.cm.cm_to_svg(pt.cm.hotwater,'hotwater.svg',stops=5)
+
diff --git a/examples/color.py b/examples/color.py
index 91f8bbf..b8296db 100644
--- a/examples/color.py
+++ b/examples/color.py
@@ -12,39 +12,37 @@
n = len(pt.color.cycle)
for i in range(n):
- plt.plot(x,np.sin(x-i*2*np.pi/n),label=pt.color.cycle[i],linewidth=2)
-
+ plt.plot(x,np.sin(x-i*2*np.pi/n),label=pt.color.cycle[i],linewidth=2)
+
plt.legend()
-# some lines
+# lines light and dark
plt.figure()
x = np.linspace(0,2*np.pi,100)
-plt.plot(x,np.sin(x- 0*2*np.pi/12),color=pt.color['l'],label='l')
-plt.plot(x,np.sin(x- 2*2*np.pi/12),color=pt.color['b'],label='b')
-plt.plot(x,np.sin(x- 4*2*np.pi/12),color=pt.color['r'],label='r')
-plt.plot(x,np.sin(x- 6*2*np.pi/12),color=pt.color['y'],label='y')
-plt.plot(x,np.sin(x- 8*2*np.pi/12),color=pt.color['o'],label='o')
-plt.plot(x,np.sin(x-10*2*np.pi/12),color=pt.color['g'],label='g')
+for i,key in enumerate( pt.color.keys() ):
+ plt.plot(x,np.sin(x- i*2*2*np.pi/12),color=pt.color[key],label=key)
+ plt.plot(x,np.sin(x- i*2*2*np.pi/12)-0.1,color=pt.color.light[key])
+ plt.plot(x,np.sin(x- i*2*2*np.pi/12)+0.1,color=pt.color.dark[key])
plt.legend()
# bar plot
-pt.lightcolor.reset_index()
+pt.color.reset_index()
plt.figure()
x = np.arange(6)
-cat = ['cat1','cat2','cat3','cat4','cat5','cat6','cat7','cat8']
+cat = ['cat{}'.format(i+1) for i in range(n)]
p = []
yy = np.zeros_like(x)
for c in cat:
- y = np.random.random_integers(1,high=10,size=x.shape)
- pl = plt.bar(x, y, 0.8, bottom=yy , color=pt.lightcolor.next())
- p.append(pl[0])
- yy = yy+y
-
+ y = np.random.random_integers(1,high=10,size=x.shape)
+ pl = plt.bar(x, y, 0.8, bottom=yy , color=pt.color.next())
+ p.append(pl[0])
+ yy = yy+y
+
plt.legend(p,cat)
# show plots
-plt.show()
\ No newline at end of file
+plt.show()
diff --git a/examples/color.svg b/examples/color.svg
deleted file mode 100644
index e45ffea..0000000
--- a/examples/color.svg
+++ /dev/null
@@ -1,1460 +0,0 @@
-
-
-
-
diff --git a/examples/cs_to_svg.py b/examples/cs_to_svg.py
new file mode 100644
index 0000000..8efddc9
--- /dev/null
+++ b/examples/cs_to_svg.py
@@ -0,0 +1,6 @@
+
+import plottools as pt
+
+pt.color.to_svg('plottools_default_color.svg')
+pt.color.light.to_svg('plottools_default_light_color.svg')
+pt.color.dark.to_svg('plottools_default_dark_color.svg')
\ No newline at end of file
diff --git a/examples/grayscale.py b/examples/grayscale.py
deleted file mode 100644
index c3e6f18..0000000
--- a/examples/grayscale.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import matplotlib.pyplot as plt
-import matplotlib.patches as patches
-import numpy as np
-
-import plottools as pt
-
-
-# get the grayscale values of all colors
-n = len(pt.color.cycle)
-
-colors = []
-names = []
-grayscales = []
-for i in range(n):
- names.append( pt.color.cycle[i] )
- colors.append( pt.color[i] )
- grayscales.append( 0.21*pt.color[i][0] + 0.72*pt.color[i][1] + 0.07*pt.color[i][2] )
-
-colors = np.array(colors)
-names = np.array(names)
-grayscales = np.array(grayscales)
-
-
-index = np.argsort( grayscales )
-
-# create a figure with the colors sorted by grayscale
-fig = plt.figure()
-ax = fig.add_subplot(111)
-for i,ind in enumerate(index):
- ax.add_patch( patches.Rectangle( (i, 0), 1.0, 5.0, facecolor=colors[ind,:], edgecolor='none') )
-
-plt.xlim([0,n])
-plt.ylim([0,5])
-
-
-# plot the grayscale values in order
-plt.figure()
-plt.plot(np.arange(n),grayscales[index],'o',color=pt.color['k'])
-for i,ind in enumerate(index):
- plt.text(i+0.12,grayscales[ind],names[ind])
-
-plt.xlim([0,n])
-plt.ylim([0.0,0.9])
-
-plt.show()
-
-
diff --git a/examples/greyscale.py b/examples/greyscale.py
new file mode 100644
index 0000000..4fa5f39
--- /dev/null
+++ b/examples/greyscale.py
@@ -0,0 +1,57 @@
+import matplotlib.pyplot as plt
+import matplotlib.patches as patches
+import numpy as np
+
+import plottools as pt
+from plottools.cs.util import to_greyscale
+
+# get the greyscale values of all colors
+n = len(pt.color.cycle)
+
+colors = []
+names = []
+greyscales = []
+for i in range(n):
+ names.append( pt.color.cycle[i] )
+ colors.append( pt.color[i] )
+ greyscales.append( np.mean(to_greyscale(pt.color[i])) )
+
+colors = np.array(colors)
+names = np.array(names)
+greyscales = np.array(greyscales)
+
+
+index = np.argsort( greyscales )
+
+# create a figure with the colors sorted by greyscale
+fig = plt.figure()
+ax = fig.add_subplot(111)
+for i,ind in enumerate(index):
+ ax.add_patch( patches.Rectangle( (i, 0), 1.0, 5.0, facecolor=colors[ind,:], edgecolor='none') )
+
+plt.xlim([0,n])
+plt.ylim([0,5])
+
+# create a figure with the colors sorted by greyscale as lines
+fig = plt.figure()
+ax = fig.add_subplot(111)
+for i,ind in enumerate(index):
+ plt.plot( [0.0,5.0],[i+0.5,i+0.5], linewidth=2, color=colors[ind,:], label=names[ind] )
+
+plt.legend()
+plt.xlim([0,5])
+plt.ylim([0,n])
+
+
+# plot the greyscale values in order
+plt.figure()
+plt.plot(np.arange(n),greyscales[index],'o',color=pt.color['k'])
+for i,ind in enumerate(index):
+ plt.text(i+0.12,greyscales[ind],names[ind])
+
+plt.xlim([0,n])
+plt.ylim([0.0,0.9])
+
+plt.show()
+
+
diff --git a/examples/plottools_default_color.svg b/examples/plottools_default_color.svg
new file mode 100644
index 0000000..bc68faf
--- /dev/null
+++ b/examples/plottools_default_color.svg
@@ -0,0 +1,72 @@
+
+
+
+
diff --git a/examples/plottools_default_dark_color.svg b/examples/plottools_default_dark_color.svg
new file mode 100644
index 0000000..7b6d1ab
--- /dev/null
+++ b/examples/plottools_default_dark_color.svg
@@ -0,0 +1,72 @@
+
+
+
+
diff --git a/examples/plottools_default_light_color.svg b/examples/plottools_default_light_color.svg
new file mode 100644
index 0000000..438ee64
--- /dev/null
+++ b/examples/plottools_default_light_color.svg
@@ -0,0 +1,72 @@
+
+
+
+
diff --git a/examples/print_scan_default.jpg b/examples/print_scan_default.jpg
new file mode 100644
index 0000000..0571712
Binary files /dev/null and b/examples/print_scan_default.jpg differ
diff --git a/examples/print_scan_default.py b/examples/print_scan_default.py
new file mode 100644
index 0000000..2e51d59
--- /dev/null
+++ b/examples/print_scan_default.py
@@ -0,0 +1,14 @@
+import matplotlib.pyplot as plt
+import plottools as pt
+
+
+
+pt.cs.prepare_print_scan(pt.color.colors)
+plt.savefig('print_scan_default.jpg')
+
+# print the image and then scan it
+pt.cs.analyse_print_scan('print_scan_default_inkjet.jpg',pt.color.colors)
+
+pt.cs.analyse_print_scan('print_scan_default_laserjet.jpg',pt.color.colors)
+
+plt.show()
diff --git a/examples/print_scan_default_inkjet.jpg b/examples/print_scan_default_inkjet.jpg
new file mode 100644
index 0000000..2315478
Binary files /dev/null and b/examples/print_scan_default_inkjet.jpg differ
diff --git a/examples/print_scan_default_laserjet.jpg b/examples/print_scan_default_laserjet.jpg
new file mode 100644
index 0000000..723f615
Binary files /dev/null and b/examples/print_scan_default_laserjet.jpg differ
diff --git a/examples/style.py b/examples/style.py
new file mode 100644
index 0000000..ba55440
--- /dev/null
+++ b/examples/style.py
@@ -0,0 +1,54 @@
+import matplotlib.pyplot as plt
+import matplotlib as mpl
+import numpy as np
+
+import plottools as pt
+pt.set_publication_rc()
+
+
+# bar chart
+plt.figure()
+pt.color.reset_index()
+x = np.arange(6)
+cat = ['cat1','cat2','cat3','cat4']
+
+p = []
+yy = np.zeros_like(x)
+for c in cat:
+ y = np.random.random_integers(1,high=10,size=x.shape)
+ plt.bar(x, y, 0.8, bottom=yy, color=pt.color.next(),label=c )
+ yy += y
+
+plt.xticks(x+0.4,x)
+plt.xlim([x[0]-0.2, x[-1]+0.8+0.2])
+
+plt.ylabel('y-label')
+plt.legend(framealpha=0.7)
+pt.style.set(['horizontalgrid','noxticks'])
+
+plt.savefig('style_horizontalgridwithoutticks.pdf')
+
+
+
+# line chart
+plt.figure()
+pt.color.reset_index()
+x = np.arange(20)
+cat = ['cat1','cat2','cat3','cat4']
+
+for c in cat:
+ y = np.cumsum( np.random.random_integers(-3,high=5,size=x.shape) )
+ plt.plot(x, y, color=pt.color.next(), label=c )
+
+plt.ylabel('y-label')
+plt.xlabel('x-label')
+plt.legend(framealpha=0.7,loc='upper left')
+pt.style.set(['horizontalgrid'])
+
+plt.savefig('style_horizontalgrid.pdf')
+
+
+
+
+# show plots
+plt.show()
\ No newline at end of file
diff --git a/examples/zoom_axes.py b/examples/zoom_axes.py
index 1e32287..a954087 100644
--- a/examples/zoom_axes.py
+++ b/examples/zoom_axes.py
@@ -1,4 +1,5 @@
import matplotlib.pyplot as plt
+import numpy as np
import plottools as pt
@@ -15,23 +16,40 @@
ax = plt.gca()
ax1 = pt.zoom_axes(fig,ax,[0.1,0.3],[1.0,1.2],[1.0,1.9],[1.5,2.0])
-ax1.plot([0,2],[1,2])
-ax1.plot([0,2],[2,1])
+plt.plot([0,2],[1,2])
+plt.plot([0,2],[2,1])
-# small figure, box on the right
-plt.figure(figsize=(8/2.54,6/2.54))
-plt.plot([0,2],[1,2])
-plt.plot([0,2],[2,1])
+
+# axes all around the box
+def draw_testlines():
+ t = np.linspace(0,30,500)
+ plt.plot(np.cos(2*np.pi*t/6),np.sin(2*np.pi*t/5))
+
+
+plt.figure(figsize=(16/2.54,12/2.54))
+draw_testlines()
plt.xlabel('xlabel')
plt.ylabel('ylabel')
+plt.xlim([-2.2,2.2])
+plt.ylim([-2.2,2.2])
+
fig = plt.gcf()
ax = plt.gca()
-ax1 = pt.zoom_axes(fig,ax,[1.5,1.6],[1.7,1.8],[0.15,0.9],[1.4,1.9])
-plt.plot([0,2],[1,2])
-plt.plot([0,2],[2,1])
+
+count = 0
+for dy in [0.8,0,-0.8]:
+ for dx in [-0.8,0,0.8]:
+ count+=1
+
+ if not (dx==0 and dy==0):
+
+ s = 2
+ ax1 = pt.zoom_axes(fig,ax,[-0.2+dx,0.2+dx],[-0.2+dy,0.2+dy],[-0.4+s*dx,0.4+s*dx],[-0.4+s*dy,0.4+s*dy])
+ draw_testlines()
+
# show plots
diff --git a/plottools/__init__.py b/plottools/__init__.py
index fa65ce1..8a46a18 100644
--- a/plottools/__init__.py
+++ b/plottools/__init__.py
@@ -1,22 +1,32 @@
-#!/usr/bin/python
-######################################################################################
-# Copyright 2015 Brecht Baeten
+#!/usr/bin/env/ python
+################################################################################
+# Copyright 2016 Brecht Baeten
# This file is part of plottools.
-#
+#
# plottools is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
-#
+#
# plottools 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 General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with plottools. If not, see .
-######################################################################################
+################################################################################
+
+from .__version__ import version as __version__
+from .plottools import *
+import cs
+import cm
+import style
+
+
+################################################################################
+# create default color schemes
+################################################################################
+color = cs.Colorscheme(cs.default.colors,longnames=cs.default.longnames,cycle=cs.default.cycle)
+
-from plottools import *
-from plotcolor import *
-import cm
\ No newline at end of file
diff --git a/plottools/__main__.py b/plottools/__main__.py
new file mode 100644
index 0000000..3195a58
--- /dev/null
+++ b/plottools/__main__.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env/ python
+################################################################################
+# Copyright 2016 Brecht Baeten
+# This file is part of plottools.
+#
+# plottools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# plottools 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with plottools. If not, see .
+################################################################################
+
+import sys
+import color.colorscheme
+import color.default
+import matplotlib.pyplot as plt
+
+if 'cst' in sys.argv:
+ print('Starting Color Scheme Tool')
+ cst = color.colorscheme.ColorschemeTool(color.default.colors)
+
+if 'cs' in sys.argv:
+ print('Plotting the default Color Scheme')
+ color.default.plot()
+ plt.show()
\ No newline at end of file
diff --git a/plottools/__version__.py b/plottools/__version__.py
new file mode 100644
index 0000000..6d009dd
--- /dev/null
+++ b/plottools/__version__.py
@@ -0,0 +1 @@
+version = '0.2.0'
\ No newline at end of file
diff --git a/plottools/cm/__init__.py b/plottools/cm/__init__.py
index 25c7037..5ead4ca 100644
--- a/plottools/cm/__init__.py
+++ b/plottools/cm/__init__.py
@@ -22,51 +22,51 @@
import numbers
import numpy as np
-from hotwater import hotwater
-from coldhot import coldhot
+from hotwater import test_cm as hotwater
+from coldhot import test_cm as coldhot
+from hue import test_cm as hue
+def cm_to_svg(colormap,filename,stops=3):
+ """
+ creates a svg file with the colormap defined as a gradient
+
+ Arguments:
+ colormap: a matplotlib colormap instance
+ filename: string, the file to write the svg file to
+ stops=3: int or list fo floats, number of stops to use in the gradient or the position of the stops in the range of 0.0 to 1.0
+
+ Example:
+ plottools.cm.cm_to_svg(plottools.cm.hotwater,'hotwater.svg',stops=5)
+ """
+
+ # input handling
+ if isinstance(stops, numbers.Number):
+ stops = np.linspace(0.0,1.0,max(2,stops))
+
+
+ # get the colors of the colormap at the stops
+ colors = ['#{:02x}{:02x}{:02x}'.format(int(colormap(s)[0]*255),int(colormap(s)[1]*255),int(colormap(s)[2]*255)) for s in stops]
-def cm2svg(colormap,filename,stops=3):
- """
- creates a svg file with the colormap defined as a gradient
-
- Arguments:
- colormap: a matplotlib colormap instance
- filename: string, the file to write the svg file to
- stops=3: int or list fo floats, number of stops to use in the gradient or the position of the stops in the range of 0.0 to 1.0
-
- Example:
- plottools.cm.cm2svg(plottools.cm.hotwater,'hotwater.svg',stops=5)
- """
-
- # input handling
- if isinstance(stops, numbers.Number):
- stops = np.linspace(0.0,1.0,max(2,stops))
-
-
- # get the colors of the colormap at the stops
- colors = ['#{:02x}{:02x}{:02x}'.format(int(colormap(s)[0]*255),int(colormap(s)[1]*255),int(colormap(s)[2]*255)) for s in stops]
+
+ # current path
+ modulepath = os.path.dirname(sys.modules[__name__].__file__)
-
- # current path
- modulepath = os.path.dirname(sys.modules[__name__].__file__)
+ # open the cm_blank svg file
+ blank_file = open(os.path.join(modulepath,'cm_blank.svg'), 'r')
+ content = blank_file.read()
+ blank_file.close()
- # open the cm_blank svg file
- blank_file = open(os.path.join(modulepath,'cm_blank.svg'), 'r')
- content = blank_file.read()
- blank_file.close()
+ # find the index where the stops must come
+ ind = content.find(' ')
+ before = content[:ind]
+ after = content[ind:]
+
+ new_content = before
+ for s,c in zip(stops,colors):
+ new_content = new_content + ' \n'.format(s*1000,s,c)
- # find the index where the stops must come
- ind = content.find(' ')
- before = content[:ind]
- after = content[ind:]
-
- new_content = before
- for s,c in zip(stops,colors):
- new_content = new_content + ' \n'.format(s*1000,s,c)
-
- new_content = new_content+after
-
- new_file = open(filename, 'w')
- new_file.write(new_content)
- new_file.close()
\ No newline at end of file
+ new_content = new_content+after
+
+ new_file = open(filename, 'w')
+ new_file.write(new_content)
+ new_file.close()
\ No newline at end of file
diff --git a/plottools/cm/coldhot.py b/plottools/cm/coldhot.py
index 4ccc95e..db76a1e 100644
--- a/plottools/cm/coldhot.py
+++ b/plottools/cm/coldhot.py
@@ -265,7 +265,7 @@
[ 0.89594311, 0.06741927, 0.05408478],
[ 0.8985074 , 0.06502592, 0.04455319]]
-coldhot = ListedColormap(cm_data, name=__file__)
+test_cm = ListedColormap(cm_data, name=__file__)
if __name__ == "__main__":
@@ -274,9 +274,9 @@
try:
from viscm import viscm
- viscm(coldhot)
+ viscm(test_cm)
except ImportError:
print("viscm not found, falling back on simple display")
plt.imshow(np.linspace(0, 100, 256)[None, :], aspect='auto',
- cmap=coldhot)
+ cmap=test_cm)
plt.show()
diff --git a/plottools/cm/hotwater.py b/plottools/cm/hotwater.py
index 218beb0..e5af9d7 100644
--- a/plottools/cm/hotwater.py
+++ b/plottools/cm/hotwater.py
@@ -265,7 +265,7 @@
[ 0.97993936, 0.07651969, 0.06070177],
[ 0.98318108, 0.07928898, 0.05174878]]
-hotwater = ListedColormap(cm_data, name=__file__)
+test_cm = ListedColormap(cm_data, name=__file__)
if __name__ == "__main__":
@@ -274,9 +274,9 @@
try:
from viscm import viscm
- viscm(hotwater)
+ viscm(test_cm)
except ImportError:
print("viscm not found, falling back on simple display")
plt.imshow(np.linspace(0, 100, 256)[None, :], aspect='auto',
- cmap=hotwater)
+ cmap=test_cm)
plt.show()
diff --git a/plottools/cm/hotwater_new.py b/plottools/cm/hotwater_new.py
new file mode 100644
index 0000000..319373e
--- /dev/null
+++ b/plottools/cm/hotwater_new.py
@@ -0,0 +1,282 @@
+
+from matplotlib.colors import ListedColormap
+from numpy import nan, inf
+
+# Used to reconstruct the colormap in viscm
+parameters = {'xp': [-4.9044626459399012, 9.97648973501245, 34.597701856224575, 41.361771120293838, 42.985147743670467, 40.820645579168314, 34.868264626787351, 10.517615276138002, 9.4353641938869259],
+ 'yp': [-30.735930735930708, -35.064935064935042, -19.101731601731586, -2.3268398268398016, 14.718614718614731, 23.647186147186176, 30.140692640692663, 28.78787878787881, 23.917748917748924],
+ 'min_Jp': 18.1236038719,
+ 'max_Jp': 75.7557706627}
+
+cm_data = [[ 0.05309761, 0.02765358, 0.57567508],
+ [ 0.06660787, 0.02434601, 0.57973405],
+ [ 0.0785568 , 0.02135181, 0.58346076],
+ [ 0.08939909, 0.01867147, 0.58687144],
+ [ 0.09940638, 0.01630231, 0.58998262],
+ [ 0.10875606, 0.01423902, 0.59281085],
+ [ 0.11757076, 0.01247489, 0.59537244],
+ [ 0.12592751, 0.01101599, 0.59768191],
+ [ 0.13390902, 0.00983502, 0.59975751],
+ [ 0.1415683 , 0.00892002, 0.60161437],
+ [ 0.14894758, 0.00825842, 0.60326698],
+ [ 0.15608122, 0.00783726, 0.60472917],
+ [ 0.16298542, 0.00765897, 0.60601441],
+ [ 0.16969712, 0.00769376, 0.60713563],
+ [ 0.17623673, 0.00792811, 0.60810447],
+ [ 0.18262151, 0.0083491 , 0.60893196],
+ [ 0.18886137, 0.00895062, 0.60962923],
+ [ 0.19496922, 0.00972059, 0.6102065 ],
+ [ 0.20096261, 0.01063905, 0.61067195],
+ [ 0.20685149, 0.0116947 , 0.61103397],
+ [ 0.21264204, 0.01288018, 0.61130119],
+ [ 0.21833889, 0.01418948, 0.61148226],
+ [ 0.22395598, 0.01560351, 0.61158205],
+ [ 0.22949954, 0.017113 , 0.6116068 ],
+ [ 0.23497253, 0.01871268, 0.6115635 ],
+ [ 0.24037845, 0.02039634, 0.61145852],
+ [ 0.24572711, 0.0221491 , 0.61129471],
+ [ 0.25102262, 0.02396359, 0.61107668],
+ [ 0.2562652 , 0.02583764, 0.61081062],
+ [ 0.26146007, 0.02776231, 0.61049975],
+ [ 0.26661301, 0.02972782, 0.61014626],
+ [ 0.27172685, 0.0317284 , 0.60975347],
+ [ 0.27679986, 0.03376471, 0.60932731],
+ [ 0.28183936, 0.03582485, 0.60886756],
+ [ 0.28684781, 0.03790366, 0.6083766 ],
+ [ 0.29182577, 0.0399988 , 0.60785786],
+ [ 0.29677486, 0.04205962, 0.60731377],
+ [ 0.30169931, 0.04405991, 0.60674447],
+ [ 0.30660069, 0.04600647, 0.60615172],
+ [ 0.31147816, 0.04790318, 0.60553906],
+ [ 0.31633589, 0.04974712, 0.60490571],
+ [ 0.32117539, 0.05153889, 0.60425274],
+ [ 0.32599699, 0.05328063, 0.60358207],
+ [ 0.33080215, 0.05497281, 0.60289446],
+ [ 0.33559309, 0.05661499, 0.60218974],
+ [ 0.34037066, 0.0582083 , 0.60146879],
+ [ 0.34513502, 0.05975469, 0.60073301],
+ [ 0.34988845, 0.06125339, 0.59998161],
+ [ 0.35463173, 0.06270541, 0.59921506],
+ [ 0.35936533, 0.06411205, 0.59843401],
+ [ 0.36409055, 0.06547354, 0.59763816],
+ [ 0.36880824, 0.06679056, 0.59682752],
+ [ 0.37351904, 0.06806398, 0.59600219],
+ [ 0.37822389, 0.06929426, 0.59516186],
+ [ 0.38292342, 0.07048213, 0.59430642],
+ [ 0.38761812, 0.07162843, 0.59343582],
+ [ 0.39230921, 0.07273308, 0.59254913],
+ [ 0.39699686, 0.07379718, 0.59164645],
+ [ 0.40168146, 0.07482151, 0.5907276 ],
+ [ 0.40636404, 0.07580601, 0.58979155],
+ [ 0.41104513, 0.07675118, 0.58883778],
+ [ 0.41572466, 0.07765821, 0.58786642],
+ [ 0.42040316, 0.07852746, 0.58687679],
+ [ 0.42508217, 0.07935804, 0.58586694],
+ [ 0.42976084, 0.08015195, 0.58483778],
+ [ 0.43443942, 0.08090976, 0.58378881],
+ [ 0.43911958, 0.08163023, 0.58271766],
+ [ 0.44380089, 0.08231471, 0.58162458],
+ [ 0.44848307, 0.08296435, 0.58050964],
+ [ 0.45316687, 0.08357889, 0.57937146],
+ [ 0.45785367, 0.08415726, 0.57820778],
+ [ 0.46254209, 0.08470188, 0.57702 ],
+ [ 0.46723226, 0.08521324, 0.57580751],
+ [ 0.4719262 , 0.08568923, 0.57456694],
+ [ 0.47662293, 0.08613168, 0.57329911],
+ [ 0.48132194, 0.08654182, 0.57200418],
+ [ 0.48602379, 0.08691939, 0.57068079],
+ [ 0.49073037, 0.08726222, 0.56932546],
+ [ 0.49543959, 0.08757359, 0.56794064],
+ [ 0.50015148, 0.08785391, 0.56652563],
+ [ 0.50486763, 0.08810128, 0.56507733],
+ [ 0.50958796, 0.08831615, 0.5635951 ],
+ [ 0.51431112, 0.08850072, 0.56208028],
+ [ 0.51903711, 0.08865533, 0.56053219],
+ [ 0.52376837, 0.08877665, 0.5589461 ],
+ [ 0.5285031 , 0.08886753, 0.55732411],
+ [ 0.53324065, 0.08892916, 0.5556665 ],
+ [ 0.53798094, 0.08896186, 0.55397262],
+ [ 0.54272702, 0.08896118, 0.5522365 ],
+ [ 0.54747586, 0.08893179, 0.55046235],
+ [ 0.5522273 , 0.08887416, 0.54864969],
+ [ 0.55698123, 0.08878855, 0.54679786],
+ [ 0.56174073, 0.08867016, 0.54490051],
+ [ 0.56650253, 0.08852422, 0.54296255],
+ [ 0.57126649, 0.08835106, 0.54098339],
+ [ 0.57603245, 0.088151 , 0.53896249],
+ [ 0.58080312, 0.08791955, 0.53689381],
+ [ 0.58557589, 0.08766106, 0.53478136],
+ [ 0.59035019, 0.08737647, 0.53262529],
+ [ 0.59512585, 0.08706617, 0.53042509],
+ [ 0.59990447, 0.08672733, 0.52817666],
+ [ 0.60468542, 0.08636096, 0.52588021],
+ [ 0.60946713, 0.08596987, 0.523538 ],
+ [ 0.61424936, 0.0855545 , 0.52114958],
+ [ 0.61903188, 0.08511531, 0.51871456],
+ [ 0.62381748, 0.08464706, 0.51622597],
+ [ 0.6286029 , 0.08415581, 0.51368973],
+ [ 0.63338785, 0.08364216, 0.51110554],
+ [ 0.63817203, 0.08310674, 0.50847304],
+ [ 0.64295538, 0.08254979, 0.50579147],
+ [ 0.64774008, 0.081967 , 0.50305476],
+ [ 0.65252312, 0.08136444, 0.50026868],
+ [ 0.65730419, 0.08074292, 0.49743294],
+ [ 0.66208296, 0.08010337, 0.4945473 ],
+ [ 0.6668591 , 0.07944675, 0.49161152],
+ [ 0.67163395, 0.07877048, 0.4886212 ],
+ [ 0.67640634, 0.07807733, 0.48557796],
+ [ 0.68117498, 0.07737055, 0.48248389],
+ [ 0.68593948, 0.07665153, 0.47933882],
+ [ 0.69069946, 0.07592179, 0.47614262],
+ [ 0.69545451, 0.07518297, 0.47289514],
+ [ 0.70020514, 0.07443463, 0.46959368],
+ [ 0.70495119, 0.07367802, 0.46623733],
+ [ 0.70969093, 0.07291827, 0.46282941],
+ [ 0.71442389, 0.07215771, 0.45936986],
+ [ 0.7191496 , 0.07139891, 0.45585863],
+ [ 0.72386756, 0.07064463, 0.4522957 ],
+ [ 0.72857726, 0.0698979 , 0.44868106],
+ [ 0.73327815, 0.06916198, 0.44501469],
+ [ 0.73797067, 0.0684378 , 0.44129359],
+ [ 0.74265376, 0.06773039, 0.43751914],
+ [ 0.74732625, 0.06704543, 0.4336932 ],
+ [ 0.7519875 , 0.0663874 , 0.42981587],
+ [ 0.75663688, 0.06576107, 0.42588722],
+ [ 0.76127372, 0.06517155, 0.42190739],
+ [ 0.76589734, 0.0646243 , 0.41787648],
+ [ 0.77050701, 0.0641251 , 0.41379466],
+ [ 0.77510198, 0.06368005, 0.40966209],
+ [ 0.77968148, 0.0632956 , 0.40547895],
+ [ 0.78424469, 0.06297845, 0.40124544],
+ [ 0.78879077, 0.06273562, 0.3969618 ],
+ [ 0.79331883, 0.06257433, 0.39262826],
+ [ 0.79782796, 0.06250201, 0.38824511],
+ [ 0.80231719, 0.0625262 , 0.38381264],
+ [ 0.80678552, 0.06265454, 0.37933118],
+ [ 0.81123191, 0.06289467, 0.37480109],
+ [ 0.81565526, 0.06325413, 0.37022275],
+ [ 0.82005444, 0.06374032, 0.36559658],
+ [ 0.82442824, 0.06436042, 0.36092305],
+ [ 0.82877542, 0.06512124, 0.35620264],
+ [ 0.83309468, 0.06602922, 0.3514359 ],
+ [ 0.83738465, 0.0670903 , 0.34662342],
+ [ 0.84164393, 0.06830988, 0.34176582],
+ [ 0.84587101, 0.06969273, 0.33686379],
+ [ 0.85006436, 0.07124301, 0.33191806],
+ [ 0.85422236, 0.07296419, 0.32692944],
+ [ 0.85834332, 0.07485904, 0.32189878],
+ [ 0.86242549, 0.07692968, 0.31682702],
+ [ 0.86646704, 0.07917755, 0.31171513],
+ [ 0.87046607, 0.08160343, 0.30656421],
+ [ 0.87442119, 0.08420662, 0.30137077],
+ [ 0.87833001, 0.08698756, 0.29613785],
+ [ 0.88219009, 0.08994599, 0.2908692 ],
+ [ 0.88599921, 0.09308064, 0.28556626],
+ [ 0.88975507, 0.09638984, 0.28023058],
+ [ 0.89345533, 0.09987153, 0.2748638 ],
+ [ 0.89709804, 0.10352348, 0.26946107],
+ [ 0.90068031, 0.10734356, 0.26402769],
+ [ 0.90419933, 0.11132898, 0.25856871],
+ [ 0.90765249, 0.11547665, 0.25308626],
+ [ 0.91103723, 0.11978374, 0.24757981],
+ [ 0.91435089, 0.12424795, 0.24204682],
+ [ 0.91759042, 0.12886475, 0.23649776],
+ [ 0.92075309, 0.13363034, 0.23093538],
+ [ 0.92383614, 0.13854243, 0.22535634],
+ [ 0.92683669, 0.14359677, 0.21976584],
+ [ 0.92975211, 0.14878801, 0.21417154],
+ [ 0.93257967, 0.15411281, 0.20857341],
+ [ 0.9353166 , 0.15956763, 0.20297249],
+ [ 0.93796078, 0.1651455 , 0.19737897],
+ [ 0.94050971, 0.1708426 , 0.19179353],
+ [ 0.94296107, 0.17665432, 0.18621905],
+ [ 0.9453134 , 0.18257312, 0.18066473],
+ [ 0.94756459, 0.18859492, 0.17513136],
+ [ 0.94971337, 0.19471298, 0.16962553],
+ [ 0.95175887, 0.20092016, 0.16415405],
+ [ 0.95369972, 0.20721165, 0.15871899],
+ [ 0.95553588, 0.21357931, 0.15332885],
+ [ 0.95726703, 0.22001679, 0.14798859],
+ [ 0.95889329, 0.22651724, 0.1427042 ],
+ [ 0.96041508, 0.23307393, 0.13748175],
+ [ 0.96183341, 0.2396794 , 0.13232879],
+ [ 0.96314915, 0.24632745, 0.12725128],
+ [ 0.9643634 , 0.25301197, 0.12225572],
+ [ 0.96547871, 0.25972444, 0.11735313],
+ [ 0.96649572, 0.2664613 , 0.1125477 ],
+ [ 0.96741698, 0.2732154 , 0.10785035],
+ [ 0.96824515, 0.27998027, 0.10327209],
+ [ 0.96898111, 0.28675336, 0.09881957],
+ [ 0.96962858, 0.29352721, 0.09450826],
+ [ 0.97018982, 0.30029774, 0.09035031],
+ [ 0.97066588, 0.30706328, 0.08635642],
+ [ 0.97106075, 0.3138174 , 0.08254584],
+ [ 0.97137701, 0.32055663, 0.07893557],
+ [ 0.97161529, 0.3272808 , 0.07554017],
+ [ 0.9717784 , 0.33398642, 0.07238057],
+ [ 0.97187122, 0.34066724, 0.06948243],
+ [ 0.97189286, 0.34732594, 0.0668615 ],
+ [ 0.97184539, 0.35396069, 0.06454003],
+ [ 0.97173225, 0.36056797, 0.06254211],
+ [ 0.97155612, 0.36714556, 0.06088925],
+ [ 0.97131664, 0.3736954 , 0.05959704],
+ [ 0.97101554, 0.38021654, 0.05868167],
+ [ 0.97065469, 0.38670795, 0.05815594],
+ [ 0.97023823, 0.39316591, 0.05803019],
+ [ 0.96976463, 0.39959372, 0.05830448],
+ [ 0.96923523, 0.40599107, 0.05897716],
+ [ 0.96865129, 0.41235775, 0.06004171],
+ [ 0.96801398, 0.41869364, 0.06148723],
+ [ 0.96732635, 0.42499659, 0.06329967],
+ [ 0.96658718, 0.43126916, 0.06546049],
+ [ 0.9657973 , 0.43751154, 0.06795034],
+ [ 0.96495753, 0.44372393, 0.07074881],
+ [ 0.96406862, 0.44990655, 0.07383522],
+ [ 0.96313126, 0.45605966, 0.07718926],
+ [ 0.96214662, 0.46218303, 0.0807914 ],
+ [ 0.96111424, 0.46827798, 0.08462368],
+ [ 0.96003459, 0.47434484, 0.08866957],
+ [ 0.9589081 , 0.48038396, 0.09291423],
+ [ 0.95773516, 0.48639566, 0.09734451],
+ [ 0.95651613, 0.49238026, 0.10194901],
+ [ 0.95525034, 0.49833892, 0.10671854],
+ [ 0.95393783, 0.5042721 , 0.11164532],
+ [ 0.95257931, 0.51017962, 0.11672276],
+ [ 0.95117503, 0.51606169, 0.121946 ],
+ [ 0.94972529, 0.52191839, 0.12731163],
+ [ 0.94823044, 0.52774975, 0.13281765],
+ [ 0.94668519, 0.53356011, 0.1384677 ],
+ [ 0.94509381, 0.53934613, 0.14426019],
+ [ 0.94345723, 0.54510718, 0.15019731],
+ [ 0.94177633, 0.55084259, 0.15628289],
+ [ 0.94004523, 0.55655653, 0.16252849],
+ [ 0.93826433, 0.56224826, 0.16894303],
+ [ 0.93644119, 0.56791198, 0.17553146],
+ [ 0.93457307, 0.5735491 , 0.18231101],
+ [ 0.93264972, 0.57916572, 0.18931129],
+ [ 0.93069317, 0.58474582, 0.19653516],
+ [ 0.92868255, 0.59030183, 0.20403697],
+ [ 0.92664509, 0.59581404, 0.21183032],
+ [ 0.92456249, 0.60129151, 0.21999539],
+ [ 0.92247312, 0.60670657, 0.22856835],
+ [ 0.92039123, 0.61204575, 0.23764896],
+ [ 0.918349 , 0.61728213, 0.24737718],
+ [ 0.91645204, 0.62234256, 0.25788678],
+ [ 0.91489602, 0.62709876, 0.26932624],
+ [ 0.91410321, 0.63129811, 0.28157637]]
+
+test_cm = ListedColormap(cm_data, name=__file__)
+
+
+if __name__ == "__main__":
+ import matplotlib.pyplot as plt
+ import numpy as np
+
+ try:
+ from viscm import viscm
+ viscm(test_cm)
+ except ImportError:
+ print("viscm not found, falling back on simple display")
+ plt.imshow(np.linspace(0, 100, 256)[None, :], aspect='auto',
+ cmap=test_cm)
+ plt.show()
diff --git a/plottools/cm/hue.py b/plottools/cm/hue.py
new file mode 100644
index 0000000..7cbb6c8
--- /dev/null
+++ b/plottools/cm/hue.py
@@ -0,0 +1,27 @@
+
+import numpy as np
+from matplotlib.colors import ListedColormap
+from colorspacious import cspace_converter
+
+
+_JCh_to_sRGB1 = cspace_converter('JCh', 'sRGB1')
+
+cm_data = []
+for i in range(256):
+ RGBi = np.clip(_JCh_to_sRGB1( (50,100,360.*i/255) ),0,1)
+ cm_data.append( list(RGBi) )
+
+test_cm = ListedColormap(cm_data, name=__file__)
+
+
+if __name__ == "__main__":
+ import matplotlib.pyplot as plt
+
+ try:
+ from viscm import viscm
+ viscm(test_cm)
+ except ImportError:
+ print("viscm not found, falling back on simple display")
+ plt.imshow(np.linspace(0, 100, 256)[None, :], aspect='auto',
+ cmap=test_cm)
+ plt.show()
diff --git a/plottools/cs/__init__.py b/plottools/cs/__init__.py
new file mode 100644
index 0000000..fdac77e
--- /dev/null
+++ b/plottools/cs/__init__.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env/ python
+################################################################################
+# Copyright 2016 Brecht Baeten
+# This file is part of plottools.
+#
+# plottools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# plottools 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with plottools. If not, see .
+################################################################################
+
+from .colorscheme import *
+from .util import *
+from . import default
\ No newline at end of file
diff --git a/plottools/cs/colorscheme.py b/plottools/cs/colorscheme.py
new file mode 100644
index 0000000..b10afdd
--- /dev/null
+++ b/plottools/cs/colorscheme.py
@@ -0,0 +1,380 @@
+#!/usr/bin/env/ python
+################################################################################
+# Copyright 2016 Brecht Baeten
+# This file is part of plottools.
+#
+# plottools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# plottools 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with plottools. If not, see .
+################################################################################
+
+import sys
+import os
+import numpy as np
+import matplotlib.pyplot as plt
+import matplotlib.patches as patches
+from matplotlib.widgets import Button, Slider
+
+from cycler import cycler
+
+import util
+from .. import cm
+
+################################################################################
+# Colorscheme class
+################################################################################
+class Colorscheme(object):
+ """
+ A colorscheme class useful for plotting
+
+ """
+
+ def __init__(self,colors,longnames=None,cycle=None,base=None):
+ """
+ defines a colorscheme object useful for plotting
+
+ Parameters
+ ----------
+ colors : dict
+ Dictionary of named colors as RGB (0-1) tupples
+
+ longnames : dict, optional
+ Dictionary of long names of the colors specified in colors
+
+ cycle : list, optional
+ List with the cycle order
+
+ Examples
+ --------
+ >>> cs = Colorscheme({'r':(1.,0.,0.),'g':(0.,1.,0.),'b':(0.,0.,1.)},['b','g','r'])
+ >>> print( cs[0] )
+ >>>
+ >>> print( cs.next() )
+ >>> print( cs.next() )
+ >>>
+ >>> cs.reset_index()
+ >>> print( cs.next() )
+ """
+
+
+ if longnames == None:
+ longnames = {k:k for k in colors.keys()}
+
+ if cycle == None:
+ cycle = colors.keys()
+
+
+ self.colors = colors
+ self.longnames = longnames
+ self.cycle = cycle
+ self.currentindex = 0
+ self.base = base
+
+ # create light and dark variants
+ if base==None:
+ lightcolors = {}
+ darkcolors = {}
+ for key,val in colors.items():
+ JCh = util._sRGB1_to_JCh(val)
+ JCh[0] = min(JCh[0]+30,100)
+ lightcolors[key] = np.clip(util._JCh_to_sRGB1(JCh),0,1)
+
+ JCh = util._sRGB1_to_JCh(val)
+ JCh[0] = max(JCh[0]-20,1e-6)
+ darkcolors[key] = np.clip(util._JCh_to_sRGB1(JCh),0,1)
+
+ self.light = Colorscheme(lightcolors,longnames=longnames,cycle=cycle,base=self)
+ self.dark = Colorscheme(darkcolors,longnames=longnames,cycle=cycle,base=self)
+
+
+ def next(self):
+ """
+ get the next color in the cycle
+ """
+ c = self.colors[self.cycle[self.currentindex]]
+ self.currentindex += 1
+ if self.currentindex >= len(self.cycle):
+ self.currentindex = 0
+
+ return c
+
+ def reset_index(self):
+ """
+ resets the current color index to 0
+ """
+
+ self.currentindex = 0
+
+ def set_as_default(self):
+ """
+ sets the colorscheme as the default color cycle in matplotlib figures
+ """
+
+ plt.rc('axes',prop_cycle=cycler('color', [self.colors[c] for c in self.cycle]) )
+
+ def __getitem__(self,key):
+ if isinstance(key,int):
+ self.currentindex = key+1
+ return self.colors[self.cycle[key]]
+ else:
+ if key in self.longnames:
+ key = self.longnames[key]
+
+ self.currentindex = self.cycle.index(key)+1
+ return self.colors[key]
+
+ def keys(self):
+ return self.colors.keys()
+
+ def values(self):
+ return [self.colors[key] for key in self.keys()]
+
+ def items(self):
+ return zip(self.keys(),self.values())
+
+ def plot(self,ax=None):
+ """
+ plots the current colorscheme main colors
+ """
+ if ax == None:
+ fig,ax = plt.subplots()
+
+ util.plot_colors(ax,self.colors,order=self.cycle)
+
+ def to_svg(self,filename):
+ """
+ converts the colorscheme to an svg file with block of colors ordered
+ according to the cycle
+
+ parameters
+ ----------
+ filename : string
+ the file to write the svg file to
+
+ Examples
+ --------
+ >>>> plottools.colors.to_svg('defaultcolors.svg')
+
+ """
+
+ # get the colors of the colorscheme
+ colors = ['#{:02x}{:02x}{:02x}'.format(int(self.colors[k][0]*255),int(self.colors[k][1]*255),int(self.colors[k][2]*255)) for k in self.cycle]
+
+ width = 100./len(self.cycle)
+ x = [i*width for i in range(len(self.cycle))]
+
+ # current path
+ modulepath = os.path.dirname(sys.modules[__name__].__file__)
+
+ # open the cs_blank svg file
+ blank_file = open(os.path.join(modulepath,'cs_blank.svg'), 'r')
+ content = blank_file.read()
+ blank_file.close()
+
+ # find the rectangle template
+ ind0 = content.find(' ')
+ before = content[:ind0]
+ after = content[ind1:]
+
+
+ new_content = before
+ for k,c,x in zip(self.cycle,colors,x):
+ new_content = new_content + ' \n'.format(c,k,width,x)
+
+ new_content = new_content+after
+
+ new_file = open(filename, 'w')
+ new_file.write(new_content)
+ new_file.close()
+
+
+
+
+class ColorschemeTool(object):
+ """
+ """
+ def __init__(self,colorscheme):
+ """
+ """
+
+ if colorscheme.__class__ == Colorscheme:
+ colors = colorscheme.colors
+ else:
+ colors = colorscheme
+
+ self.order = util.order_by_J(colors)
+ self.RGB = colors
+ self.grey = self._to_greyscale(colors)
+ self.exampledata = self._generate_exampledata(self.order)
+
+
+ plt.figure(figsize=(12,10))
+ axcolor = 'None'
+
+ x = 0.70
+ y = 0.40
+ w = 0.10
+ h = 0.50
+
+ self.ax_col = plt.axes([x, y, w, h])
+ self.ax_col.get_xaxis().set_visible(False)
+ self.ax_col.get_yaxis().set_visible(False)
+
+ self.ax_grey = plt.axes([x+w+0.05, y, w, h])
+ self.ax_grey.get_xaxis().set_visible(False)
+ self.ax_grey.get_yaxis().set_visible(False)
+
+
+ self.ax_distribution = plt.axes([0.05, 0.05, 0.45, y-0.10])
+ self.ax_example_color = plt.axes([0.55, 0.23, 0.40, 0.12])
+ self.ax_example_grey = plt.axes([0.55, 0.05, 0.40, 0.12])
+
+
+ ax_btn_print_rgb = plt.axes([0.70, 0.92, 0.25, 0.05])
+ self.btn_print_rgb = Button(ax_btn_print_rgb, 'Print RGB values')
+ self.btn_print_rgb.on_clicked(self.print_rgb)
+
+ # create initial plots
+ self.plot_colors()
+ self.plot_distribution()
+ self.plot_example()
+
+
+ # create JCh sliders
+ self.slider_J = {}
+ self.slider_C = {}
+ self.slider_h = {}
+
+ for i,key in enumerate(self.order):
+ RGB = self.RGB[key]
+ JCh = util._sRGB1_to_JCh(RGB)
+
+ # J
+ aJ = plt.axes([0.05, y+i*h/len(self.order)+0.02, x-0.15, 0.05])
+ aJ.imshow( np.linspace(0, 100, 101).reshape(1, -1), cmap='gray' )
+ sJ = Slider(aJ, r'J', 0, 100, valinit=JCh[0])
+ sJ.on_changed(self._slider_update)
+
+ self.slider_J[key] = sJ
+
+ # C
+ aC = plt.axes([0.05, y+i*h/len(self.order)+0.00, x-0.15, 0.05])
+ aC.imshow( np.linspace(0, 100, 101).reshape(1, -1), cmap='gray' )
+ sC = Slider(aC, r'C', 0, 100, valinit=JCh[1])
+ sC.on_changed(self._slider_update)
+
+ self.slider_C[key] = sC
+
+ # h
+ ah = plt.axes([0.05, y+i*h/len(self.order)-0.02, x-0.15, 0.05])
+ ah.imshow( np.linspace(0, 100, 101).reshape(1, -1), cmap=cm.hue )
+ sh = Slider(ah, r'h', 0, 100, valinit=JCh[2]/360.*100.)
+ sh.on_changed(self._slider_update)
+
+ self.slider_h[key] = sh
+
+ plt.show()
+
+ def plot_colors(self):
+
+ # clear the axes
+ self.ax_col.cla()
+ self.ax_grey.cla()
+
+ # draw patches
+ for i,key in enumerate(self.order):
+ self.ax_col.add_patch( patches.Rectangle( (0, i), 1.0, 1.0, facecolor=self.RGB[key], edgecolor='none') )
+ self.ax_col.text(0.5,i+0.5,key,ha='center')
+
+ self.ax_grey.add_patch( patches.Rectangle( (0, i), 1.0, 1.0, facecolor=self.grey[key], edgecolor='none') )
+ self.ax_grey.text(0.5,i+0.5,key,ha='center')
+
+
+ self.ax_col.set_xlim([0,1])
+ self.ax_col.set_ylim([0,len(self.order)])
+
+ self.ax_grey.set_xlim([0,1])
+ self.ax_grey.set_ylim([0,len(self.order)])
+
+ def plot_distribution(self):
+
+ self.ax_distribution.cla()
+
+ x = np.arange(len(self.order))
+ y = np.array([ np.mean(util.to_greyscale(self.RGB[key])) for key in self.order])
+
+ # add a line connecting 1st and last point
+ self.ax_distribution.plot([x[0],x[-1]],[y[0],y[-1]],'b--')
+
+ # add greyscale points
+ self.ax_distribution.plot(x,y,'bo',label='greyscale')
+
+ # add J points
+ j = np.array([ 1.*util._sRGB1_to_JCh(self.RGB[key])[0]/100 for key in self.order])
+ self.ax_distribution.plot([x[0],x[-1]],[j[0],j[-1]],'r--')
+ self.ax_distribution.plot(x,j,'rs',label='lightness')
+
+ # add labels
+ for xi,yi,key in zip(x,j,self.order):
+ self.ax_distribution.text(xi,yi-0.10,key)
+
+ self.ax_distribution.set_xlim([-0.5,len(self.order)-1+0.5])
+ self.ax_distribution.set_ylim([0.,1.])
+
+ self.ax_distribution.legend(loc='lower right',numpoints=1)
+
+
+ def plot_example(self):
+
+ self.ax_example_color.cla()
+ for key in self.order:
+ self.ax_example_color.plot(self.exampledata[key],color=self.RGB[key],linewidth=2,label=key)
+ #self.ax_example_color.legend()
+
+ self.ax_example_grey.cla()
+ for key in self.order:
+ self.ax_example_grey.plot(self.exampledata[key],color=self.grey[key],linewidth=2,label=key)
+ #self.ax_example_grey.legend()
+
+ def print_rgb(self,event):
+ print('')
+ print('RGB values:')
+ for k,c in self.RGB.items():
+ print( '\'{}\': ({:>3.0f}./255, {:>3.0f}./255, {:>3.0f}./255),'.format(k,c[0]*255,c[1]*255,c[2]*255))
+
+ def _generate_exampledata(self,keys):
+ exampledata = {}
+ for i,key in enumerate(keys):
+ exampledata[key] = np.cumsum(np.random.random(100)-0.5) + i
+ return exampledata
+
+ def _to_greyscale(self,colors):
+ grey = {}
+ for key,val in colors.items():
+ grey[key] = util.to_greyscale(val)
+
+ return grey
+
+ def _slider_update(self, val):
+ for key in self.order:
+ jp = self.slider_J[key].val
+ cp = self.slider_C[key].val
+ hp = self.slider_h[key].val*360./100.
+
+ self.RGB[key] = np.clip(util._JCh_to_sRGB1([jp,cp,hp]),0,1)
+
+ self.grey = self._to_greyscale(self.RGB)
+
+ self.plot_colors()
+ self.plot_distribution()
+ self.plot_example()
diff --git a/plottools/cs/cs_blank.svg b/plottools/cs/cs_blank.svg
new file mode 100644
index 0000000..b86ea9b
--- /dev/null
+++ b/plottools/cs/cs_blank.svg
@@ -0,0 +1,30 @@
+
+
+
+
diff --git a/plottools/cs/default.py b/plottools/cs/default.py
new file mode 100644
index 0000000..e7b6410
--- /dev/null
+++ b/plottools/cs/default.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env/ python
+################################################################################
+# Copyright 2016 Brecht Baeten
+# This file is part of plottools.
+#
+# plottools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# plottools 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with plottools. If not, see .
+################################################################################
+
+
+# colors that look nice and have considerable contrast in greyscale
+colors = {
+ 'b': ( 71./255, 116./255, 190./255),
+ 'g': (110./255, 197./255, 128./255),
+ 'k': ( 51./255, 51./255, 51./255),
+ 'o': (255./255, 185./255, 59./255),
+ 'p': (118./255, 56./255, 119./255),
+ 'r': (242./255, 92./255, 61./255),
+ 'y': (248./255, 232./255, 73./255),
+}
+
+longnames = {
+ 'black': 'k',
+ 'purple': 'p',
+ 'blue': 'b',
+ 'green': 'g',
+ 'red': 'r',
+ 'orange': 'o',
+ 'yellow': 'y'
+}
+
+cycle = ['b','g','r','o','y','p','k']
\ No newline at end of file
diff --git a/plottools/cs/util.py b/plottools/cs/util.py
new file mode 100644
index 0000000..5407613
--- /dev/null
+++ b/plottools/cs/util.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env/ python
+################################################################################
+# Copyright 2016 Brecht Baeten
+# This file is part of plottools.
+#
+# plottools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# plottools 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with plottools. If not, see .
+################################################################################
+
+import numpy as np
+import matplotlib.pyplot as plt
+import matplotlib.patches as patches
+from colorspacious import cspace_converter
+from PIL import Image
+
+
+
+def plot_colors(ax,colors,order=None):
+
+ if order == None:
+ order = colors.keys()
+ elif order == 'J':
+ order = order_by_J(colors)
+
+ for i,key in enumerate(order):
+ ax.add_patch( patches.Rectangle( (i, 0), 1.0, 1.0, facecolor=colors[key], edgecolor='none') )
+ ax.text(i+0.5,0.5,key,ha='center')
+
+ plt.xlim([0,len(colors)])
+ plt.ylim([0,1])
+
+
+# derived from matplotlib viscm
+_sRGB1_to_JCh = cspace_converter('sRGB1', 'JCh')
+_JCh_to_sRGB1 = cspace_converter('JCh', 'sRGB1')
+
+def to_greyscale(sRGB1):
+ JCh = _sRGB1_to_JCh(sRGB1)
+ JCh[..., 1] = 0
+ return _JCh_to_sRGB1(JCh)
+
+
+def change_lightness_to_match_greyscale(col,grey):
+ JCh = _sRGB1_to_JCh(col)
+ Js = np.linspace(1e-6,100,20)
+ gs = []
+
+ for Ji in Js:
+ JCh[..., 0] = Ji
+ RGBi = _JCh_to_sRGB1(JCh)
+ RGBi = np.clip(RGBi,0,1)
+ gi = np.mean(to_greyscale(RGBi))
+
+ gs.append(gi)
+
+ J = np.interp(grey,gs,Js)
+ JCh[..., 0] = J
+ newcol = _JCh_to_sRGB1( JCh )
+ newcol = np.clip(newcol,0,1)
+
+ return newcol
+
+def order_by_J(colors):
+ keys = colors.keys()
+ J = [_sRGB1_to_JCh(colors[key])[0] for key in keys]
+ order = [k for (j,k) in sorted(zip(J,keys))]
+
+ return order
+
+def prepare_print_scan(colors):
+
+ order = order_by_J(colors)
+
+ colors_grey = {}
+
+ for key,val in colors.items():
+ colors_grey[key] = to_greyscale(val)
+
+ fig = plt.figure(figsize=(16./2.54,24./2.54))
+ ax1 = fig.add_subplot(211)
+ plot_colors(ax1,colors,order)
+ plt.title('print color')
+
+ ax2 = fig.add_subplot(212)
+ plot_colors(ax2,colors_grey,order)
+ plt.title('print greyscale')
+
+
+def analyse_print_scan(scanfile,colors):
+
+ im = Image.open(scanfile).convert('RGB')
+
+ # order the colors according to lightness
+ keys = colors.keys()
+ J = [_sRGB1_to_JCh(colors[key])[0] for key in keys]
+ order = [k for (j,k) in sorted(zip(J,keys))]
+
+ width,height = im.size
+
+ scan_colors = {}
+ scan_colors_grey = {}
+ for i,key in enumerate(order):
+ # get the colors from the scan file
+ x = int(0.20*width + 0.66*i/len(order)*width)
+
+ y = int(0.25*height)
+ r = [ im.getpixel( (xi, yi) )[0] for xi in np.array(x)+np.arange(-int(0.02*width),int(0.02*width)) for yi in np.array(y)+np.arange(-int(0.02*height),int(0.02*height)) ]
+ g = [ im.getpixel( (xi, yi) )[1] for xi in np.array(x)+np.arange(-int(0.02*width),int(0.02*width)) for yi in np.array(y)+np.arange(-int(0.02*height),int(0.02*height)) ]
+ b = [ im.getpixel( (xi, yi) )[2] for xi in np.array(x)+np.arange(-int(0.02*width),int(0.02*width)) for yi in np.array(y)+np.arange(-int(0.02*height),int(0.02*height)) ]
+
+
+ scan_colors[key] = (np.mean(r)/255,np.mean(g)/255,np.mean(b)/255)
+
+ y = int(0.80*height)
+ r = [ im.getpixel( (xi, yi) )[0] for xi in np.array(x)+np.arange(-int(0.02*width),int(0.02*width)) for yi in np.array(y)+np.arange(-int(0.02*height),int(0.02*height)) ]
+ g = [ im.getpixel( (xi, yi) )[1] for xi in np.array(x)+np.arange(-int(0.02*width),int(0.02*width)) for yi in np.array(y)+np.arange(-int(0.02*height),int(0.02*height)) ]
+ b = [ im.getpixel( (xi, yi) )[2] for xi in np.array(x)+np.arange(-int(0.02*width),int(0.02*width)) for yi in np.array(y)+np.arange(-int(0.02*height),int(0.02*height)) ]
+
+ scan_colors_grey[key] = (np.mean(r)/255,np.mean(g)/255,np.mean(b)/255)
+
+
+ fig = plt.figure(figsize=(16./2.54,24./2.54))
+ ax1 = fig.add_subplot(211)
+ plot_colors(ax1,scan_colors,order)
+ plt.title('scan color')
+
+ ax2 = fig.add_subplot(212)
+ plot_colors(ax2,scan_colors_grey,order)
+ plt.title('scan greyscale')
+
+
+ # compare the printed values with to_greyscale
+ greyscales = {}
+ for key,val in colors.items():
+ greyscales[key] = np.mean(to_greyscale(val))
+
+ scan_greyscales= {}
+ for key,val in scan_colors.items():
+ scan_greyscales[key] = np.mean(val)
+
+
+ # rescale so first and last value are equal
+ x = np.arange(len(order))
+ y1 = np.array([greyscales[key] for key in order])
+ y2 = np.array([scan_greyscales[key] for key in order])
+ # rescale so first and last value are equal
+ y2 = min(y1) + (y2-min(y2))*(max(y1)-min(y1))/(max(y2)-min(y2))
+
+ plt.figure()
+ plt.plot([x[0],x[-1]],[y1[0],y1[-1]],'k--')
+
+ plt.plot(x,y1,'o',color='b',label='greyscale')
+ plt.plot(x,y2,'s',color='r',label='printed')
+
+ for xi,yi,key in zip(x,y1,order):
+ plt.text(xi,yi-0.10,key)
+
+ plt.legend(loc='lower right',numpoints=1)
+ plt.xlim([-0.5,len(order)-1+0.5])
+ plt.ylim([-0.02,1.02])
+
\ No newline at end of file
diff --git a/plottools/linearize_grayscale.py b/plottools/linearize_grayscale.py
deleted file mode 100644
index 0f247d8..0000000
--- a/plottools/linearize_grayscale.py
+++ /dev/null
@@ -1,139 +0,0 @@
-import numpy as np
-import matplotlib.pyplot as plt
-import matplotlib.patches as patches
-import colorsys
-
-
-
-def plot_colors(ax,coldict,order={}):
- n = len(coldict.keys())
-
- if order == {}:
- for i,k in enumerate(coldict.keys()):
- order[k] = i
-
- for k,c in coldict.items():
- o = order[k]
- ax.add_patch( patches.Rectangle( (o, 0), 1.0, 5.0, facecolor=c, edgecolor='none') )
- ax.text(o+0.5,2.5,k,ha='center')
-
- plt.xlim([0,n])
- plt.ylim([0,5])
-
-
-def rgb_to_gray(r,g,b):
- return 0.21*r + 0.72*g + 0.07*b
-
-
-def change_lightness_to_match_grayscale(col,gray):
- h,l,s = colorsys.rgb_to_hls(col[0],col[1],col[2])
-
- ls = np.linspace(0,1,100)
- gs = []
-
- for li in ls:
- ri,gi,bi = colorsys.hls_to_rgb(h,li,s)
- gs.append(rgb_to_gray(ri,gi,bi))
-
-
- ll = np.interp(gray,gs,ls)
-
- newcol = colorsys.hls_to_rgb(h,ll,s)
-
- return newcol
-
-
-
-
-def linearize_grayscale(colors_rgb,gray_min,gray_max,order={},plot=False):
- # convert all colors ro grayscale and determine the order
- colors_gray_rgb = {}
- gray = []
- keys = []
- for k,c in colors_rgb.items():
- g = rgb_to_gray(c[0],c[1],c[2])
- keys.append(k)
- gray.append(g)
- colors_gray_rgb[k] = (g,g,g)
-
-
- # define the order
- if order == {}:
- for i,o in enumerate(np.argsort(gray)):
- order[keys[o]] = i
-
-
-
-
- # rescale to grayscale min-max
- gray_edit = np.linspace(gray_min,gray_max,len(order.keys()))
- colors_edit_rgb = {}
- for k,c in colors_rgb.items():
- colors_edit_rgb[k] = change_lightness_to_match_grayscale(c,gray_edit[order[k]])
-
-
- # convert all colors ro grayscale and determine the order
- colors_edit_gray_rgb = {}
- for k,c in colors_edit_rgb.items():
- g = rgb_to_gray(c[0],c[1],c[2])
- colors_edit_gray_rgb[k] = (g,g,g)
-
-
- # print final colors
- if plot:
- for k,c in colors_edit_rgb.items():
- print( '\'{}\': ({:>3.0f}./255,{:>3.0f}./255,{:>3.0f}./255),'.format(k,c[0]*255,c[1]*255,c[2]*255))
-
- fig = plt.figure()
- ax1 = fig.add_subplot(221)
- plot_colors(ax1,colors_rgb,order)
- plt.title('original')
-
- ax2 = fig.add_subplot(223)
- plot_colors(ax2,colors_gray_rgb,order)
-
- ax3 = fig.add_subplot(222)
- plot_colors(ax3,colors_edit_rgb,order)
- plt.title('edited')
-
- ax4 = fig.add_subplot(224)
- plot_colors(ax4,colors_edit_gray_rgb,order)
-
-
- return colors_edit_rgb
-
-
-if __name__ == '__main__':
-
- colors_rgb = {
- 'r': (245./255, 82./255, 45./255),
- 'o': (244./255,154./255, 26./255),
- 'y': (242./255,244./255, 66./255),
- 'g': ( 32./255, 81./255, 37./255),
- 'l': ( 80./255,180./255, 54./255),
- 'a': ( 95./255,173./255,209./255),
- 'b': ( 31./255, 57./255,101./255),
- 'p': (114./255, 31./255,119./255),
- 'k': ( 49./255, 49./255, 49./255)
- }
-
- order = {
- 'k': 0,
- 'p': 1,
- 'b': 2,
- 'r': 3,
- 'g': 4,
- 'o': 5,
- 'l': 6,
- 'a': 7,
- 'y': 8
- }
-
- print('\nbase colors:')
- linearize_grayscale(colors_rgb,0.20,0.90,order=order,plot=True)
-
- print('\nlight colors:')
- linearize_grayscale(colors_rgb,0.40,0.95,order=order,plot=True)
-
-
- plt.show()
diff --git a/plottools/plotcolor.py b/plottools/plotcolor.py
deleted file mode 100644
index 285b4b4..0000000
--- a/plottools/plotcolor.py
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/python
-################################################################################
-# Copyright 2015 Brecht Baeten
-# This file is part of plottools.
-#
-# plottools is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# plottools 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with plottools. If not, see .
-################################################################################
-
-import matplotlib.pyplot as plt
-from cycler import cycler
-
-################################################################################
-# Color definitions
-################################################################################
-# colors that look ok and have considerable contrast in grayscale
-basecolors = {
- 'a': (175./255,214./255,232./255),
- 'b': ( 54./255,100./255,177./255),
- 'g': ( 66./255,168./255, 77./255),
- 'k': ( 51./255, 51./255, 51./255),
- 'l': (126./255,210./255,104./255),
- 'o': (244./255,152./255, 23./255),
- 'p': (153./255, 42./255,160./255),
- 'r': (245./255, 87./255, 51./255),
- 'y': (241./255,243./255, 53./255)
-}
-
-lightcolors = {
- 'a': (204./255,229./255,240./255),
- 'b': (101./255,141./255,208./255),
- 'g': (107./255,197./255,116./255),
- 'k': (102./255,102./255,102./255),
- 'l': (167./255,224./255,152./255),
- 'o': (247./255,183./255, 91./255),
- 'p': (204./255, 86./255,211./255),
- 'r': (248./255,132./255,106./255),
- 'y': (248./255,249./255,154./255)
-}
-
-longnames = {
- 'black': 'k',
- 'purple': 'p',
- 'blue': 'b',
- 'green': 'g',
- 'red': 'r',
- 'orange': 'o',
- 'lime': 'l',
- 'aqua': 'a',
- 'yellow': 'y'
-}
-
-basecycle = ['b','r','g','o','y','p','l','a','k']
-
-
-################################################################################
-# Colorscheme class
-################################################################################
-class Colorscheme(object):
- def __init__(self,colors=basecolors,longnames=longnames,cycle=basecycle):
- """
- defines a colorscheme object useful for plotting
-
- Arguments:
- colors: dict, dictionary of named colors as RGB (0-1) tupples
- cycle: list, list with the cycle order
-
- Example:
- cs = Colorscheme({'r':(1.,0.,0.),'g':(0.,1.,0.),'b':(0.,0.,1.)},['b','g','r'])
- print( cs[0] )
-
- print( cs.next() )
- print( cs.next() )
-
- cs.reset_index()
- print( cs.next() )
- """
-
- self.colors = colors
- self.longnames = longnames
- self.cycle = cycle
- self.currentindex = 0
-
- def next(self):
- """
- get the next color in the cycle
- """
- c = self.colors[self.cycle[self.currentindex]]
- self.currentindex += 1
- if self.currentindex >= len(self.cycle):
- self.currentindex = 0
-
- return c
-
- def reset_index(self):
- """
- resets the current color index to 0
- """
-
- self.currentindex = 0
-
- def set_as_default(self):
- """
- sets the colorscheme as the default color cycle in matplotlib figures
- """
-
- plt.rc('axes',prop_cycle=cycler('color', [self.colors[c] for c in self.cycle]) )
-
- def __getitem__(self,key):
- if isinstance(key,int):
- self.currentindex = key+1
- return self.colors[self.cycle[key]]
- else:
- if key in self.longnames:
- key = self.longnames[key]
-
- self.currentindex = self.cycle.index(key)+1
- return self.colors[key]
-
-
-
-################################################################################
-# create default color schemes
-################################################################################
-color = Colorscheme()
-lightcolor = Colorscheme(colors=lightcolors)
diff --git a/plottools/plottools.py b/plottools/plottools.py
index 3144c4c..2e0f1d7 100644
--- a/plottools/plottools.py
+++ b/plottools/plottools.py
@@ -1,64 +1,262 @@
-#!/usr/bin/python
+#!/usr/bin/env/ python
################################################################################
-# Copyright 2015 Brecht Baeten
+# Copyright 2016 Brecht Baeten
# This file is part of plottools.
-#
+#
# plottools is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
-#
+#
# plottools 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 General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with plottools. If not, see .
################################################################################
import matplotlib.pyplot as plt
import numpy as np
+import itertools
def set_publication_rc():
+ """
+ Sets rc parameters for creating plots suitable for publication
+
+ Notes
+ -----
+ The computer modern fonts are not installed by default on windows. But can
+ be downloaded at https://sourceforge.net/projects/cm-unicode/
+ To use new installed fonts in matplotlib you must delete the font cache file
+ located at C:\Users\yourusername\.matplotlib
+
+ Examples
+ --------
+ .. plot::
+
+ >>> import matplotlib.pyplot as plt
+ >>> import numpy as np
+ >>> import plottools
+ >>>
+ >>> plottools.set_publication_rc()
+ >>> plt.plot(np.arange(10),10*np.random.random(10))
+ >>> plt.xlabel('x-label')
+ >>> plt.ylabel('y-label')
+ >>> plt.show()
+
+ """
+
# figure
- plt.rc('figure', autolayout=True)
- plt.rc('savefig', format='pdf', dpi=150, bbox='tight', pad_inches=0.01)
+ plt.rc('figure', autolayout=True, figsize=(80/25.4,50/25.4))
+ plt.rc('savefig', format='pdf', dpi=150, bbox='tight', pad_inches=0.02)
+
+ # text
+ #plt.rc('text', usetex=True)
+
# font
- plt.rc('font', family='serif', serif=['computer modern roman'], size=6)
+ plt.rc('font', size=6)
+ plt.rc('font', **{'family':'sans-serif', 'sans-serif':['computer modern sans serif', 'CMU Sans Serif'], 'serif':['computer modern roman', 'CMU Serif']} )
+
# axes
plt.rc('axes', linewidth=0.4, labelsize=8)
plt.rc('axes.formatter', useoffset=False)
+
# legend
plt.rc('legend', fontsize=8, frameon=True)
+
# lines
plt.rc('lines', linewidth=0.8,markersize=4)
- # text
- plt.rc('text', usetex=True)
+
+ # patch
+ plt.rc('patch', linewidth=0.4, edgecolor=(1.0,1.0,1.0))
+
# ticks
- plt.rc('xtick.major', size=3, width=0.3, pad=3)
- plt.rc('ytick.major', size=3, width=0.3, pad=3)
- plt.rc('xtick.minor', size=2, width=0.3, pad=3)
- plt.rc('ytick.minor', size=2, width=0.3, pad=3)
+ plt.rc('xtick.major', size=2, width=0.3, pad=3)
+ plt.rc('ytick.major', size=2, width=0.3, pad=3)
+ plt.rc('xtick.minor', size=1, width=0.3, pad=3)
+ plt.rc('ytick.minor', size=1, width=0.3, pad=3)
-def zoom_axes(fig,ax,zoom_x,zoom_y,axes_x,axes_y,box=True,box_color='k',box_alpha=0.8,connect=True,connect_color='k',connect_alpha=0.3,spacing_zoom=2,spacing_axes=20):
+def savefig(filename,width=None,height=None,ratio=8./5.,**kwargs):
"""
- Creates a new axes which zooms in on a part of a given axes
+ Creates a new figure with a specified width and 8:5, width:height ratio
+
+ If no width or height are specified a 8cm x 5cm figure is saved. If the
+ width and height are specified, a figure of that size is saved.
+ If one of width or height is specified, the ratio is used to define the
+ other.
+
+ Parameters
+ ----------
+ width : number
+ figure width in cm
+
+ height : number
+ figure height in cm
+
+ ratio : number
+ figure height in cm
+
+ """
+
+ if width is None and height is None:
+ width = 8.
+ height = width/ratio
+ elif not width is None and height is None:
+ height = width/ratio
+ elif not height is None and width is None:
+ width = height*ratio
+
+
+ plt.gcf().set_size_inches(width/2.54,height/2.54)
+ plt.savefig(filename,**kwargs)
+
+
+def set_style(style,axes=None):
+ """
+ Sets the style of a single axes object from some specification on top of other rc parameters
+
+ Parameters
+ ----------
+ style : {'horizontalgrid','horizontalgridwithoutticks'}
+ style string 'horizontalgrid','horizontalgridwithoutticks'
+
+ axes : matplotlib axes object
+ the axes to which to apply the style, if omitted the current axis
+ obtained with plt.gca() it styled
+
+ Examples
+ --------
+ >>> plt.plot(np.arange(10),10*np.random.random(10))
+ >>> plottools.set_style('horizontalgrid')
+ >>> plt.show()
+
+ """
+
+ if axes == None:
+ axes = plt.gca()
+
+ if style in ['horizontalgrid','horizontalgridwithoutticks']:
+ # hide the spines except the bottom one
+ axes.spines['top'].set_visible(False)
+ # axes.spines['bottom'].set_visible(False)
+ axes.spines['right'].set_visible(False)
+ axes.spines['left'].set_visible(False)
- Arguments:
- fig: matplotlib figure
- ax: matplotlib axes
- zoom_x: list, specifying the zooming horizontal area
- zoom_y: list, specifying the zooming vertical area
- axes_x: list, specifying the new axes horizontal location in data coordinates
- axes_y: list, specifying the new axes vertical location in data coordinates
+ # show ticks only on the left bottom
+ axes.get_xaxis().tick_bottom()
+ axes.get_yaxis().tick_left()
+
+ # add horizontal lines
+ yticks = axes.get_yticks()
+ xlim = axes.get_xlim()
+
+ for y in yticks:
+ axes.plot(xlim, [y,y], '-', linewidth=0.3, color='k', alpha=0.3, zorder=-10)
+
+ axes.yaxis.set_tick_params(which='both', bottom='off', top='off', labelbottom='on', left='off', right='off', labelleft='on')
+
+
+ if style == 'horizontalgridwithoutticks':
+ axes.xaxis.set_tick_params(which='both', bottom='off', top='off', labelbottom='on', left='off', right='off', labelleft='on')
+
+
+
+def zoom_axes(fig,ax,zoom_x,zoom_y,axes_x,axes_y,box=True,box_color='k',box_alpha=0.8,connect=True,connect_color='k',connect_alpha=0.3,spacing=4,tick_width=20,tick_height=12):
+ """
+ Creates a new axes which zooms in on a part of a given axes.
+
+ A box is drawn around the area to be zoomed specified in data coordinates. A
+ new empty axes is created at the specified location, supplied in data
+ coordinates. The new axis limits are set so that they match the zoom box.
+
+ The zoom box and axis can be connected with two lines, connecting the outer
+ most corner points while leaving space for the axis ticks.
+
- Returns:
- ax1: a new axes
+ Parameters
+ ----------
+ fig : matplotlib figure
+ the figure in which to create a zoom axis
+
+ ax : matplotlib axes
+ the axis in which to create a zoom axis
+
+ zoom_x : list
+ [min, max] specifying the zooming horizontal area in data
+ coordinates
+
+ zoom_y : list
+ [min, max] specifying the zooming vertical area in data coordinates
+
+ axes_x : list
+ [min, max] specifying the new axes horizontal location in data
+ coordinates
+
+ axes_y : list
+ [min, max] specifying the new axes vertical location in data
+ coordinates
+
+ box : bool, optional
+ specifies whether a box is drawn
+
+ box_color : color string or tuple,optional
+ specifies the box color
+
+ box_alpha : number
+ between 0 and 1, specifies the box alpha
- Example:
+ connect : bool, optional
+ specifies whether the connecting lines are drawn
+
+ connect_color : color string or tuple,optional
+ specifies the connecting lines color
+
+ connect_alpha : number
+ between 0 and 1, specifies the connecting lines alpha
+
+ spacing : number
+ specifies the spacing between the box, axis and the connecting lines
+ in points
+
+ tick_width : number
+ specifies the width of the tick labels in points to avoid drawing
+ connecting lines through the tick labels
+
+ tick_height : number
+ specifies the height of the tick labels in points to avoid drawing
+ connecting lines through the tick labels
+
+
+ Returns
+ -------
+ ax_zoom : matplotlib axes
+ the new axes
+ Notes
+ -----
+ * Axes limits should not be changed after a zoom axes has been added
+ * :code:`zoom_axes` does not give the expected results when used on a
+ subfigure
+
+ Examples
+ --------
+ .. plot::
+
+ >>> import matplotlib.pyplot as plt
+ >>> import numpy as np
+ >>> import plottools
+ >>>
+ >>> fig,ax = plt.subplots()
+ >>> x = np.linspace(0,1,100)
+ >>> y = 1-x + 0.02*(2*np.random.random(len(x))-1)
+ >>> ax.plot(x,y)
+ >>> ax_zoom = plottools.zoom_axes(fig,ax,[0.1,0.2],[0.8,0.9],[0.6,0.9],[0.6,0.9])
+ >>> ax_zoom.plot(x,y)
+ >>> plt.show()
+
"""
plt.tight_layout()
@@ -79,35 +277,356 @@ def zoom_axes(fig,ax,zoom_x,zoom_y,axes_x,axes_y,box=True,box_color='k',box_alph
ax.plot([zoom_x[0],zoom_x[1],zoom_x[1],zoom_x[0],zoom_x[0]],[zoom_y[0],zoom_y[0],zoom_y[1],zoom_y[1],zoom_y[0]],color=box_color,alpha=box_alpha,linewidth=0.4)
if connect:
- # estimate the width of the ticks
- spacing_zoom = (ax.transData.inverted()).transform_point((spacing_zoom,0))[0]-(ax.transData.inverted()).transform_point((0,0))[0]
- spacing_axes = (ax.transData.inverted()).transform_point((spacing_axes,0))[0]-(ax.transData.inverted()).transform_point((0,0))[0]
-
- if zoom_x[1] < axes_x[0]:
- # the zoom box is on the left
- x_connect_bot = [zoom_x[1] + spacing_zoom , axes_x[0] - 0.3*spacing_axes]
- x_connect_top = [zoom_x[1] + spacing_zoom , axes_x[0] - 0.3*spacing_axes]
- y_connect_bot = np.interp(x_connect_bot,[zoom_x[1],axes_x[0]],[zoom_y[0],axes_y[0]])
- y_connect_top = np.interp(x_connect_top,[zoom_x[1],axes_x[0]],[zoom_y[1],axes_y[1]])
-
- if y_connect_bot[1] > axes_y[0]:
- x_connect_bot = [zoom_x[1] + spacing_zoom , axes_x[0] - spacing_axes]
- y_connect_bot = np.interp(x_connect_bot,[zoom_x[1],axes_x[0]],[zoom_y[0],axes_y[0]])
-
- if y_connect_top[1] < axes_y[1]:
- x_connect_top = [zoom_x[1] + spacing_zoom , axes_x[0] - spacing_axes]
- y_connect_top = np.interp(x_connect_top,[zoom_x[1],axes_x[0]],[zoom_y[1],axes_y[1]])
+
+ # define a box of points of the axes and the zoom
+ zoom_xx = [zoom_x[0],zoom_x[0],zoom_x[1],zoom_x[1]]
+ zoom_yy = [zoom_y[0],zoom_y[1],zoom_y[1],zoom_y[0]]
+ axes_xx = [axes_x[0],axes_x[0],axes_x[1],axes_x[1]]
+ axes_yy = [axes_y[0],axes_y[1],axes_y[1],axes_y[0]]
+
+ # determine which points to connect
+ if axes_x[1] < zoom_x[1]:
+ # left
+ if axes_y[0] > zoom_y[0]:
+ # top
+ p1 = 0
+ p2 = 2
+ elif axes_y[1] < zoom_y[1]:
+ # bottom
+ p1 = 1
+ p2 = 3
+ else:
+ # center
+ p1 = 2
+ p2 = 3
+
+ elif axes_x[0] > zoom_x[0]:
+ # right
+ if axes_y[0] > zoom_y[0]:
+ # top
+ p1 = 1
+ p2 = 3
+ elif axes_y[1] < zoom_y[1]:
+ # bottom
+ p1 = 0
+ p2 = 2
+ else:
+ # center
+ p1 = 0
+ p2 = 1
else:
- # the zoom box is on the right
- # the zoom box is on the left
- x_connect_bot = [axes_x[1] + 0.3*spacing_axes , zoom_x[0] - spacing_zoom]
- x_connect_top = [axes_x[1] + 0.3*spacing_axes , zoom_x[0] - spacing_zoom]
- y_connect_bot = np.interp(x_connect_bot,[axes_x[1],zoom_x[0]],[axes_y[0],zoom_y[0]])
- y_connect_top = np.interp(x_connect_top,[axes_x[1],zoom_x[0]],[axes_y[1],zoom_y[1]])
-
- ax.plot(x_connect_bot,y_connect_bot,color=connect_color,alpha=connect_alpha,linewidth=0.4)
- ax.plot(x_connect_top,y_connect_top,color=connect_color,alpha=connect_alpha,linewidth=0.4)
+ # center
+ if axes_y[0] > zoom_y[0]:
+ # top
+ p1 = 0
+ p2 = 3
+ elif axes_y[1] < zoom_y[1]:
+ # bottom
+ p1 = 1
+ p2 = 2
+ else:
+ # center, the axes is over the zoom
+ p1 = 0
+ p2 = 0
+
+
+ line1 = ([zoom_xx[p1],axes_xx[p1]],[zoom_yy[p1],axes_yy[p1]])
+ line2 = ([zoom_xx[p2],axes_xx[p2]],[zoom_yy[p2],axes_yy[p2]])
+
+
+ # estimate the width and height of the ticks
+ tick_width = (ax.transData.inverted()).transform_point((tick_width,0))[0]-(ax.transData.inverted()).transform_point((0,0))[0]
+ tick_height = (ax.transData.inverted()).transform_point((0,tick_height))[1]-(ax.transData.inverted()).transform_point((0,0))[1]
+ spacing = (ax.transData.inverted()).transform_point((spacing,0))[0]-(ax.transData.inverted()).transform_point((0,0))[0]
+
+ # create fictional boxes around the axes where no lines should be
+ box_axes_x = [ axes_x[0]-tick_width , axes_x[1]+spacing]
+ box_axes_y = [ axes_y[0]-tick_height , axes_y[1]+spacing]
+
+ box_zoom_x = [ zoom_x[0]-spacing , zoom_x[1]+spacing]
+ box_zoom_y = [ zoom_y[0]-spacing , zoom_y[1]+spacing]
+
+
+
+ # cut the lines inside the boxes
+ t = np.linspace(0,1,100)
+
+ line1_cut = line1
+ line2_cut = line2
+ for tt in t:
+ x = line1[0][0]*(1-tt) + line1[0][1]*tt
+ y = line1[1][0]*(1-tt) + line1[1][1]*tt
+ if x <= box_zoom_x[0] or x >= box_zoom_x[1] or y <= box_zoom_y[0] or y >= box_zoom_y[1]:
+ line1_cut[0][0] = x
+ line1_cut[1][0] = y
+ break
+
+ for tt in t[::-1]:
+ x = line1[0][0]*(1-tt) + line1[0][1]*tt
+ y = line1[1][0]*(1-tt) + line1[1][1]*tt
+ if (x <= box_axes_x[0] or x >= box_axes_x[1]) or (y <= box_axes_y[0] or y >= box_axes_y[1]):
+ line1_cut[0][1] = x
+ line1_cut[1][1] = y
+ break
+
+ for tt in t:
+ x = line2[0][0]*(1-tt) + line2[0][1]*tt
+ y = line2[1][0]*(1-tt) + line2[1][1]*tt
+ if (x <= box_zoom_x[0] or x >= box_zoom_x[1]) or (y <= box_zoom_y[0] or y >= box_zoom_y[1]):
+ line2_cut[0][0] = x
+ line2_cut[1][0] = y
+ break
+
+ for tt in t[::-1]:
+ x = line2[0][0]*(1-tt) + line2[0][1]*tt
+ y = line2[1][0]*(1-tt) + line2[1][1]*tt
+ if (x <= box_axes_x[0] or x >= box_axes_x[1]) or (y <= box_axes_y[0] or y >= box_axes_y[1]):
+ line2_cut[0][1] = x
+ line2_cut[1][1] = y
+ break
+
+ # draw the connecting lines
+ ax.plot(line1_cut[0],line1_cut[1],color=connect_color,alpha=connect_alpha,linewidth=0.4)
+ ax.plot(line2_cut[0],line2_cut[1],color=connect_color,alpha=connect_alpha,linewidth=0.4)
+
return ax1
+
+
+
+def categorized_xticklabels(xticks,xticklabels,xticklabelnames=None,fmt=None,size=None,rotation=None,spacing=1.4):
+ """
+ Creates categorized ticks on the x-axis
+
+ Parameters
+ ----------
+ xticks : array-like
+ The x-locations of the data points
+
+ xticklabels : list of array-likes
+ A list of lists or arrays of which each must have the same length as
+ xticks. These are all used as x-tick labels, the first array is
+ displayed highest, the next arrays are printed below the previous
+ one. Results are the most appealing if the 1st array has the highest
+ variation and the last array the lowest.
+
+ xticklabelnames: list of strings, optional
+ A list of names for the labels. It must have the same length as the
+ xticklabels list.
+
+ fmt: list of fromat strings, optional
+ A list of fromatting strings as used by :code:`format` for the tick
+ labels. It must have the same length as the xticklabels list.
+
+ size: list of numbers, optional
+ A list of numbers specifying the size of the ticklabels in points.
+ It must have the same length as the xticklabels list.
+
+ rotation: list of numbers, optional
+ A list of numbers specifying the rotation of the ticklabels in
+ degrees. It must have the same length as the xticklabels list.
+
+ spacing: number, optional
+ Controls the vertical spacing between the differnt ticklabels
+
+ Examples
+ --------
+ .. plot::
+
+ >>> import matplotlib.pyplot as plt
+ >>> import numpy as np
+ >>> import plottools
+ >>>
+ >>> plottools.set_publication_rc()
+ >>>
+ >>> # generate data
+ >>> C,B,A = np.meshgrid([10,20],[0.4,0.6,0.8],[1,2,3],indexing='ij')
+ >>> xticklabels = [ A.reshape((-1,)), B.reshape((-1,)), C.reshape((-1,)) ]
+ >>> values = [np.random.random(len(t)) for t in xticklabels]
+ >>> xticks = np.arange(len(values[0]))
+ >>>
+ >>> xticklabelnames = ['coord','$B_\mathrm{value}$','CCC']
+ >>> labels = ['set1','set2','set3']
+ >>> fmt = ['${:.2f}$','${}$ m','$10^{{{:.0f}}}$']
+ >>> rotation = [70,0,0]
+ >>>
+ >>> # create the figure
+ >>> plt.figure()
+ >>> bottom = np.zeros_like(values[0])
+ >>> for val,lab in zip(values,labels):
+ ... plt.bar(xticks+0.05,val,0.9,bottom=bottom,label=lab,color=plottools.color.default.next())
+ ... bottom += val
+ ...
+ >>>
+ >>> plt.legend(framealpha=0.7,loc='upper right')
+ >>> plt.ylabel('y-label')
+ >>>
+ >>> # add categories on the x-axis
+ >>> plottools.categorized_xticklabels(xticks+0.5,xticklabels,xticklabelnames=xticklabelnames,fmt=fmt,rotation=rotation)
+ >>> plt.show()
+
+ """
+
+ # input parsing
+ if xticklabelnames == None:
+ xticklabelnames = ['']*len(xticklabels)
+
+ if fmt == None:
+ fmt = ['{}']*len(xticklabels)
+
+ if size == None:
+ size = [plt.gca().xaxis.get_major_ticks()[0].label.get_fontsize()]*len(xticklabels)
+
+ if rotation == None:
+ rotation = [0]*len(xticklabels)
+
+
+ dxticks = np.zeros_like(xticks)
+ for i in range(len(xticks)-1):
+ dxticks[i] = xticks[i+1]-xticks[i]
+ dxticks[-1] = dxticks[-2]
+
+ # set the x limits
+ plt.xlim([xticks[0]-dxticks[0]/2,xticks[-1]+dxticks[-1]/2])
+
+ # get both axis limits
+ xlim = plt.xlim()
+ ylim = plt.ylim()
+
+
+
+ # create a list of y positions in points
+ yp = []
+ ypi = -1
+
+ for i in range(len(xticklabels)):
+ ypi += -spacing*size[i] - 1*size[i]*np.sin(1.*rotation[i]/180*np.pi)
+ yp.append( ypi )
+
+ # manual xticks and labels
+ plt.xticks([])
+
+ linepositions = []
+ for i in range(len(xticklabels)-1,0,-1):
+ c = xticklabels[i]
+ xtick_old = None
+ xticklabel_old = None
+
+ for j,xtl in enumerate(c):
+ if not xtl == xticklabel_old:
+
+ if not j in linepositions:
+ # add the separator line
+ plt.annotate('',xy=(xticks[j]-0.5*dxticks[j], ylim[0]), xycoords='data',xytext=(0, yp[i]), textcoords='offset points',arrowprops={'arrowstyle':'-','color':(0.3,0.3,0.3)})
+ linepositions.append(j)
+
+ if not xticklabel_old==None:
+ # add the tick label
+ xtick_avg = 0.5*(xtick_old - 0.5*dxticks[j]) + 0.5*(xticks[j] - 0.5*dxticks[j])
+ try:
+ lab = fmt[i].format(xticklabel_old)
+ except:
+ lab = xticklabel_old
+ plt.annotate(lab,xy=(xtick_avg, ylim[0]), xycoords='data',xytext=(0, yp[i]), textcoords='offset points',ha="center", va="bottom", size=size[i], rotation=rotation[i])
+
+ xtick_old = xticks[j]
+ xticklabel_old = xtl
+
+ # add the final tick label
+ j = len(c)-1
+ xtick_avg = 0.5*(xtick_old - 0.5*dxticks[j]) + 0.5*(xticks[j] + 0.5*dxticks[j])
+ try:
+ lab = fmt[i].format(xticklabel_old)
+ except:
+ lab = xticklabel_old
+ plt.annotate(lab,xy=(xtick_avg, ylim[0]), xycoords='data',xytext=(0, yp[i]), textcoords='offset points',ha="center", va="bottom", size=size[i], rotation=rotation[i])
+
+ # add the deepest ticklabel
+ i = 0
+ for j,xtl in enumerate(xticklabels[i]):
+ try:
+ lab = fmt[i].format(xtl)
+ except:
+ lab = xtl
+ plt.annotate(lab,xy=(xticks[j], ylim[0]), xycoords='data',xytext=(0, yp[i]), textcoords='offset points',ha="center", va="bottom", size=size[i], rotation=rotation[i])
+
+ # add the final separator line
+ i = len(xticklabels)-1
+ j = len(xticklabels[-1])-1
+ plt.annotate('',xy=(xticks[j]+0.5*dxticks[j], ylim[0]), xycoords='data',xytext=(0, yp[i]), textcoords='offset points',arrowprops={'arrowstyle':'-','color':(0.3,0.3,0.3)})
+ linepositions.append(j)
+
+ # add the ticklabelnames
+ xp = 3
+ for i,l in enumerate(xticklabelnames):
+ plt.annotate(l,xy=(xlim[0], ylim[0]), xycoords='data',xytext=(-xp, yp[i]+0.1*size[i]), textcoords='offset points',ha="right", va="bottom", size=size[i])
+
+
+
+def cmapval(v,vmin=0,vmax=1,cmap=None):
+ """
+ Extracts the color belonging to a value or list of values from a colormap
+
+ Parameters
+ ----------
+ v : float, list, tuple
+ the value or values for which to return the colors
+
+ vmin : float
+ the value corresponding to the first color in the colormap
+
+ vmax : float
+ the value corresponding to the last color in the colormap
+
+ cmap : colormap
+ a matplotlib colormap
+
+ Returns
+ -------
+ hexcolor : string or list of strings
+ a html representation of the colors correspoinding to the values
+
+ Examples
+ --------
+ >>> import matplotlib.pyplot as plt
+ >>> v = cmapval(0.6)
+ '#22a784'
+
+ >>> v = cmapval([1000,2000,3000],vmin=100,vmax=3150,cmap=plt.cm.plasma)
+ ['#8f0da3', '#e46a5d', '#f7e024']
+
+ """
+
+ if cmap is None:
+ cmap = plt.cm.viridis
+
+ extractvalue = False
+ if not isinstance(v,(list,tuple)):
+ v = [v]
+ extractvalue = True
+
+ color = cmap( np.interp(v,[vmin,vmax],[0.01,0.99]) )
+ hexcolor = map(lambda rgb:'#%02x%02x%02x' % (rgb[0]*255,rgb[1]*255,rgb[2]*255),tuple(color[:,0:-1]))
+
+ if extractvalue:
+ hexcolor = hexcolor[0]
+
+ return hexcolor
+
+
+
+def marker(i):
+ """
+ Default cycle of markers
+
+ Parameters
+ ----------
+ i : int
+ an index when the supplied index is too big, the markers are cycled
+
+ """
+
+ values = ['^','s','<','o','>','*','v','1']
+ return values[np.mod(i,len(values))]
\ No newline at end of file
diff --git a/plottools/style.py b/plottools/style.py
new file mode 100644
index 0000000..03ffe4a
--- /dev/null
+++ b/plottools/style.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env/ python
+################################################################################
+# Copyright 2016 Brecht Baeten
+# This file is part of plottools.
+#
+# plottools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# plottools 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with plottools. If not, see .
+################################################################################
+
+import matplotlib.pyplot as plt
+
+
+def horizontalgrid(axes=None):
+ """
+ Sets a style with a horizontal grid to the axes
+
+ Parameters
+ ----------
+ axes : matplotlib axes object
+ the axes to which to apply the style, if omitted the current axis
+ obtained with plt.gca() it styled
+
+ Examples
+ --------
+ .. plot::
+
+ >>> import matplotlib.pyplot as plt
+ >>> import numpy as np
+ >>> import plottools
+ >>> plt.plot(np.arange(10),10*np.random.random(10))
+ >>> plottools.style.horizontalgrid()
+ >>> plt.show()
+
+ """
+
+ if axes == None:
+ axes = plt.gca()
+
+ # hide the spines except the bottom one
+ axes.spines['top'].set_visible(False)
+ # axes.spines['bottom'].set_visible(False)
+ axes.spines['right'].set_visible(False)
+ axes.spines['left'].set_visible(False)
+
+ # show ticks only on the left bottom
+ axes.get_xaxis().tick_bottom()
+ axes.get_yaxis().tick_left()
+
+ # add horizontal lines
+ yticks = axes.get_yticks()
+ xlim = axes.get_xlim()
+
+ for y in yticks:
+ axes.plot(xlim, [y,y], '-', linewidth=0.3, color='k', alpha=0.3, zorder=-10)
+
+ axes.yaxis.set_tick_params(which='both', bottom='off', top='off', labelbottom='on', left='off', right='off', labelleft='on')
+
+
+def noxticks(axes=None):
+ """
+ Sets a style with no ticks on the x axis
+
+ Parameters
+ ----------
+ axes : matplotlib axes object
+ the axes to which to apply the style, if omitted the current axis
+ obtained with plt.gca() it styled
+
+ Examples
+ --------
+ .. plot::
+
+ >>> import matplotlib.pyplot as plt
+ >>> import numpy as np
+ >>> import plottools
+ >>> plt.plot(np.arange(10),10*np.random.random(10))
+ >>> plottools.style.noxticks()
+ >>> plt.show()
+
+ """
+ if axes == None:
+ axes = plt.gca()
+
+ axes.xaxis.set_tick_params(which='both', bottom='off', top='off', left='off', right='off', labelbottom='on', labelleft='on')
+
+
+def set(*style,**kwargs):
+ """
+ Sets styles of a single axes object
+
+ Parameters
+ ----------
+ style : string or list of strings
+ style string, if a list is supplied all styles in the list are applied
+
+ axes : matplotlib axes object
+ the axes to which to apply the style, if omitted the current axis
+ obtained with plt.gca() it styled
+
+ Examples
+ --------
+ .. plot::
+
+ >>> import matplotlib.pyplot as plt
+ >>> import numpy as np
+ >>> import plottools
+ >>> plt.plot(np.arange(10),10*np.random.random(10))
+ >>> plottools.style.set(['horizontalgrid','noxticks'])
+ >>> plt.show()
+
+ """
+
+ if not 'axes' in kwargs:
+ axes = plt.gca()
+ else:
+ axes = kwargs['axes']
+
+ #if isinstance(style, basestring):
+ # style = [style]
+
+ for s in style:
+ if s in ['horizontalgrid','horizontalgridwithoutticks']:
+ horizontalgrid(axes)
+
+ if s in ['noxticks','horizontalgridwithoutticks']:
+ noxticks(axes)
+
+
+
+
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 2c8f99a..09c3822 100644
--- a/setup.py
+++ b/setup.py
@@ -1,14 +1,30 @@
+#!/usr/bin/env/ python
from setuptools import setup, find_packages
+import os
+# retrieve the version
+try:
+ versionfile = os.path.join('plottools','__version__.py')
+ f = open( versionfile, 'r')
+ content = f.readline()
+ splitcontent = content.split('\'')
+ version = splitcontent[1]
+ f.close()
+except:
+ raise Exception('Could not determine the version from plottools/__version__.py')
+
+
+# run the setup command
setup(
name='plottools',
- version='0.1.1',
- license='GNU GENERAL PUBLIC LICENSE',
- description='Plot functions that come in handy',
- url='',
- author='Brecht Baeten',
- author_email='brecht.baeten@gmail.com',
- packages=find_packages(),
- install_requires=['matplotlib'],
- classifiers = ['Programming Language :: Python :: 2.7'],
-)
\ No newline at end of file
+ version=version,
+ license='GPLv3',
+ description='',
+ long_description=open(os.path.join(os.path.dirname(__file__), 'README.rst')).read(),
+ url='https://github.com/BrechtBa/plottools',
+ author='Brecht Baeten',
+ author_email='brecht.baeten@gmail.com',
+ packages=find_packages(),
+ install_requires=['numpy','matplotlib','colorspacious'],
+ classifiers=['Programming Language :: Python :: 2.7'],
+)
diff --git a/tests/all.py b/tests/all.py
new file mode 100644
index 0000000..46985ea
--- /dev/null
+++ b/tests/all.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env/ python
+################################################################################
+# Copyright 2016 Brecht Baeten
+# This file is part of plottools.
+#
+# plottools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# plottools 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with plottools. If not, see .
+################################################################################
+
+import unittest
+
+from test_plottools import *
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file
diff --git a/tests/test_plottools.py b/tests/test_plottools.py
new file mode 100644
index 0000000..5141f8b
--- /dev/null
+++ b/tests/test_plottools.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env/ python
+################################################################################
+# Copyright 2016 Brecht Baeten
+# This file is part of plottools.
+#
+# plottools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# plottools 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with plottools. If not, see .
+################################################################################
+
+import unittest
+
+import plottools
+
+class TestPlottools(unittest.TestCase):
+
+ def test_version(self):
+ self.assertGreater( len(plottools.__version__), 0 )
+
+if __name__ == '__main__':
+ unittest.main()