diff --git a/CHANGELOG.txt b/CHANGELOG.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a4c70caaf738208643b4f0d741097b519ff4b46e
--- /dev/null
+++ b/CHANGELOG.txt
@@ -0,0 +1,90 @@
+Version 0.11.5
+--------------
+
+    - Added subset_label_formatter parameter (PR#28)
+
+Version 0.11.4
+--------------
+
+    - Added support for Counter objects (PR#26)
+    
+Version 0.11.3
+--------------
+
+    - Tiny change in README required a version bump to upload it to PyPi.
+
+Version 0.11.2
+--------------
+
+    - Fixes issue #24
+    - Addresses Debian bug #813782
+
+Version 0.11
+------------
+
+    - Fixed issue #17. This would change the previous layout of circles in certain pathological cases.
+
+Version 0.10
+------------
+
+    - Completely rewritten the region generation logic, presumably fixing all of the problems behind issue #14
+      (and hopefully not introducing too many new bugs). The new algorithm positions the labels in a different way,
+      which may look slightly worse than the previous one in some rare cases.
+    - New kind of IPython-based tests.
+
+Version 0.9
+-----------
+
+    - Better support for weird special cases in Venn3 (i.e. one circle being completely inside another, issue #10).
+
+Version 0.8
+-----------
+
+    - Added support for Python 3.
+
+Version 0.7
+-----------
+
+    - Added the possibility to provide sets (rather than subset sizes) to venn2 and venn3
+      Thanks to https://github.com/aebrahim
+    - Functions won't bail out on sets of size 0 now (the diagrams won't look pretty, though)
+      Thanks to https://github.com/olgabot
+    - Venn2/Venn3 objects now provide information about the coordinates and radii of the circles.
+    - Utility functions added for drawing unweighed diagrams (venn2_unweighted, venn3_unweighted)
+    - Labels for zero-size sets can be switched off using a method of VennDiagram.
+    - Some general code refactoring.
+
+Version 0.6
+-----------
+
+    - Added "ax" keyword to the plotting routines to specify the axes object on which the diagram will be created.
+      Thanks goes to https://github.com/sinhrks
+
+Version 0.5
+-----------
+
+    - Fixed a bug (issue 1, "unreferenced variable 's'" in venn2 and venn2_circles)
+
+Version 0.4
+-----------
+
+    - Fixed a bug ("ValueError: to_rgba: Invalid rgba arg" when specifying lighter set colors)
+
+Version 0.3
+-----------
+
+    - Changed package name from `matplotlib.venn` to `matplotlib_venn`.
+    - Fixed up some places to comply with pep8 lint checks.
+
+Version 0.2
+-----------
+
+    - Changed parameterization of venn3 and venn3_circles (now expects 7-element vectors as arguments rather than 8-element).
+    - 2-set venn diagrams (functions venn2 and venn2_circles)
+    - Added support for non-intersecting sets ("Euler diagrams")
+    - Minor fixes here and there.
+
+Version 0.1
+-----------
+
+    - Initial version, three-circle area-weighted venn diagrams.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..5947fd74ee898ab6ea3b9e72165d3643ab731850
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include README.rst CHANGELOG.txt
\ No newline at end of file
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000000000000000000000000000000000000..d2e184cbf1086d60a82f72a92f70f75896578acb
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,154 @@
+Metadata-Version: 1.1
+Name: matplotlib-venn
+Version: 0.11.5
+Summary: Functions for plotting area-proportional two- and three-way Venn diagrams in matplotlib.
+Home-page: https://github.com/konstantint/matplotlib-venn
+Author: Konstantin Tretyakov
+Author-email: kt@ut.ee
+License: MIT
+Description: ====================================================
+        Venn diagram plotting routines for Python/Matplotlib
+        ====================================================
+        
+        .. image::  https://travis-ci.org/konstantint/matplotlib-venn.png?branch=master
+           :target: https://travis-ci.org/konstantint/matplotlib-venn
+        
+        Routines for plotting area-weighted two- and three-circle venn diagrams.
+        
+        Installation
+        ------------
+        
+        The simplest way to install the package is via ``easy_install`` or
+        ``pip``::
+        
+            $ easy_install matplotlib-venn
+        
+        Dependencies
+        ------------
+        
+        - ``numpy``,
+        - ``scipy``,
+        - ``matplotlib``.
+        
+        Usage
+        -----
+        The package provides four main functions: ``venn2``,
+        ``venn2_circles``, ``venn3`` and ``venn3_circles``.
+        
+        The functions ``venn2`` and ``venn2_circles`` accept as their only
+        required argument a 3-element list ``(Ab, aB, AB)`` of subset sizes,
+        e.g.::
+        
+            venn2(subsets = (3, 2, 1))
+        
+        and draw a two-circle venn diagram with respective region areas. In
+        the particular example, the region, corresponding to subset ``A and
+        not B`` will be three times larger in area than the region,
+        corresponding to subset ``A and B``. Alternatively, you can simply
+        provide a list of two ``set`` or ``Counter`` (i.e. multi-set) objects instead (new in version 0.7),
+        e.g.::
+        
+            venn2([set(['A', 'B', 'C', 'D']), set(['D', 'E', 'F'])])
+        
+        Similarly, the functions ``venn3`` and ``venn3_circles`` take a
+        7-element list of subset sizes ``(Abc, aBc, ABc, abC, AbC, aBC,
+        ABC)``, and draw a three-circle area-weighted venn
+        diagram. Alternatively, you can provide a list of three ``set`` or ``Counter`` objects
+        (rather than counting sizes for all 7 subsets).
+        
+        The functions ``venn2_circles`` and ``venn3_circles`` draw just the
+        circles, whereas the functions ``venn2`` and ``venn3`` draw the
+        diagrams as a collection of colored patches, annotated with text
+        labels. In addition (version 0.7+), functions ``venn2_unweighted`` and
+        ``venn3_unweighted`` draw the Venn diagrams without area-weighting.
+        
+        Note that for a three-circle venn diagram it is not in general
+        possible to achieve exact correspondence between the required set
+        sizes and region areas, however in most cases the picture will still
+        provide a decent indication.
+        
+        The functions ``venn2_circles`` and ``venn3_circles`` return the list of ``matplotlib.patch.Circle`` objects that may be tuned further
+        to your liking. The functions ``venn2`` and ``venn3`` return an object of class ``VennDiagram``,
+        which gives access to constituent patches, text elements, and (since
+        version 0.7) the information about the centers and radii of the
+        circles.
+        
+        Basic Example::
+        
+            from matplotlib_venn import venn2
+            venn2(subsets = (3, 2, 1))
+        
+        For the three-circle case::
+        
+            from matplotlib_venn import venn3
+            venn3(subsets = (1, 1, 1, 2, 1, 2, 2), set_labels = ('Set1', 'Set2', 'Set3'))
+        
+        A more elaborate example::
+        
+            from matplotlib import pyplot as plt
+            import numpy as np
+            from matplotlib_venn import venn3, venn3_circles
+            plt.figure(figsize=(4,4))
+            v = venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C'))
+            v.get_patch_by_id('100').set_alpha(1.0)
+            v.get_patch_by_id('100').set_color('white')
+            v.get_label_by_id('100').set_text('Unknown')
+            v.get_label_by_id('A').set_text('Set "A"')
+            c = venn3_circles(subsets=(1, 1, 1, 1, 1, 1, 1), linestyle='dashed')
+            c[0].set_lw(1.0)
+            c[0].set_ls('dotted')
+            plt.title("Sample Venn diagram")
+            plt.annotate('Unknown set', xy=v.get_label_by_id('100').get_position() - np.array([0, 0.05]), xytext=(-70,-70),
+                         ha='center', textcoords='offset points', bbox=dict(boxstyle='round,pad=0.5', fc='gray', alpha=0.1),
+                         arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.5',color='gray'))
+            plt.show()
+        
+        An example with multiple subplots (new in version 0.6)::
+        
+            from matplotlib_venn import venn2, venn2_circles
+            figure, axes = plt.subplots(2, 2)
+            venn2(subsets={'10': 1, '01': 1, '11': 1}, set_labels = ('A', 'B'), ax=axes[0][0])
+            venn2_circles((1, 2, 3), ax=axes[0][1])
+            venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C'), ax=axes[1][0])
+            venn3_circles({'001': 10, '100': 20, '010': 21, '110': 13, '011': 14}, ax=axes[1][1])
+            plt.show()
+        
+        Perhaps the most common use case is generating a Venn diagram given
+        three sets of objects::
+        
+            set1 = set(['A', 'B', 'C', 'D'])
+            set2 = set(['B', 'C', 'D', 'E'])
+            set3 = set(['C', 'D',' E', 'F', 'G'])
+        
+            venn3([set1, set2, set3], ('Set1', 'Set2', 'Set3'))
+            plt.show()
+        
+        
+        Questions
+        ---------
+        * If you ask your questions at `StackOverflow <http://stackoverflow.com/>`_ and tag them `matplotlib-venn <http://stackoverflow.com/questions/tagged/matplotlib-venn>`_, chances are high you'll get an answer from the maintainer of this package.
+        
+        
+        See also
+        --------
+        
+        * Report issues and submit fixes at Github:
+          https://github.com/konstantint/matplotlib-venn
+          
+          Check out the ``DEVELOPER-README.rst`` for development-related notes.
+        * Some alternative means of plotting a Venn diagram (as of
+          October 2012) are reviewed in the blog post:
+          http://fouryears.eu/2012/10/13/venn-diagrams-in-python/
+        * The `matplotlib-subsets
+          <https://pypi.python.org/pypi/matplotlib-subsets>`_ package
+          visualizes a hierarchy of sets as a tree of rectangles.
+        
+Keywords: matplotlib plotting charts venn-diagrams
+Platform: Platform Independent
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Scientific/Engineering :: Visualization
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..22c24c47ccb243eb71623b4354328b163ec1b2cf
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,136 @@
+====================================================
+Venn diagram plotting routines for Python/Matplotlib
+====================================================
+
+.. image::  https://travis-ci.org/konstantint/matplotlib-venn.png?branch=master
+   :target: https://travis-ci.org/konstantint/matplotlib-venn
+
+Routines for plotting area-weighted two- and three-circle venn diagrams.
+
+Installation
+------------
+
+The simplest way to install the package is via ``easy_install`` or
+``pip``::
+
+    $ easy_install matplotlib-venn
+
+Dependencies
+------------
+
+- ``numpy``,
+- ``scipy``,
+- ``matplotlib``.
+
+Usage
+-----
+The package provides four main functions: ``venn2``,
+``venn2_circles``, ``venn3`` and ``venn3_circles``.
+
+The functions ``venn2`` and ``venn2_circles`` accept as their only
+required argument a 3-element list ``(Ab, aB, AB)`` of subset sizes,
+e.g.::
+
+    venn2(subsets = (3, 2, 1))
+
+and draw a two-circle venn diagram with respective region areas. In
+the particular example, the region, corresponding to subset ``A and
+not B`` will be three times larger in area than the region,
+corresponding to subset ``A and B``. Alternatively, you can simply
+provide a list of two ``set`` or ``Counter`` (i.e. multi-set) objects instead (new in version 0.7),
+e.g.::
+
+    venn2([set(['A', 'B', 'C', 'D']), set(['D', 'E', 'F'])])
+
+Similarly, the functions ``venn3`` and ``venn3_circles`` take a
+7-element list of subset sizes ``(Abc, aBc, ABc, abC, AbC, aBC,
+ABC)``, and draw a three-circle area-weighted venn
+diagram. Alternatively, you can provide a list of three ``set`` or ``Counter`` objects
+(rather than counting sizes for all 7 subsets).
+
+The functions ``venn2_circles`` and ``venn3_circles`` draw just the
+circles, whereas the functions ``venn2`` and ``venn3`` draw the
+diagrams as a collection of colored patches, annotated with text
+labels. In addition (version 0.7+), functions ``venn2_unweighted`` and
+``venn3_unweighted`` draw the Venn diagrams without area-weighting.
+
+Note that for a three-circle venn diagram it is not in general
+possible to achieve exact correspondence between the required set
+sizes and region areas, however in most cases the picture will still
+provide a decent indication.
+
+The functions ``venn2_circles`` and ``venn3_circles`` return the list of ``matplotlib.patch.Circle`` objects that may be tuned further
+to your liking. The functions ``venn2`` and ``venn3`` return an object of class ``VennDiagram``,
+which gives access to constituent patches, text elements, and (since
+version 0.7) the information about the centers and radii of the
+circles.
+
+Basic Example::
+
+    from matplotlib_venn import venn2
+    venn2(subsets = (3, 2, 1))
+
+For the three-circle case::
+
+    from matplotlib_venn import venn3
+    venn3(subsets = (1, 1, 1, 2, 1, 2, 2), set_labels = ('Set1', 'Set2', 'Set3'))
+
+A more elaborate example::
+
+    from matplotlib import pyplot as plt
+    import numpy as np
+    from matplotlib_venn import venn3, venn3_circles
+    plt.figure(figsize=(4,4))
+    v = venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C'))
+    v.get_patch_by_id('100').set_alpha(1.0)
+    v.get_patch_by_id('100').set_color('white')
+    v.get_label_by_id('100').set_text('Unknown')
+    v.get_label_by_id('A').set_text('Set "A"')
+    c = venn3_circles(subsets=(1, 1, 1, 1, 1, 1, 1), linestyle='dashed')
+    c[0].set_lw(1.0)
+    c[0].set_ls('dotted')
+    plt.title("Sample Venn diagram")
+    plt.annotate('Unknown set', xy=v.get_label_by_id('100').get_position() - np.array([0, 0.05]), xytext=(-70,-70),
+                 ha='center', textcoords='offset points', bbox=dict(boxstyle='round,pad=0.5', fc='gray', alpha=0.1),
+                 arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.5',color='gray'))
+    plt.show()
+
+An example with multiple subplots (new in version 0.6)::
+
+    from matplotlib_venn import venn2, venn2_circles
+    figure, axes = plt.subplots(2, 2)
+    venn2(subsets={'10': 1, '01': 1, '11': 1}, set_labels = ('A', 'B'), ax=axes[0][0])
+    venn2_circles((1, 2, 3), ax=axes[0][1])
+    venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C'), ax=axes[1][0])
+    venn3_circles({'001': 10, '100': 20, '010': 21, '110': 13, '011': 14}, ax=axes[1][1])
+    plt.show()
+
+Perhaps the most common use case is generating a Venn diagram given
+three sets of objects::
+
+    set1 = set(['A', 'B', 'C', 'D'])
+    set2 = set(['B', 'C', 'D', 'E'])
+    set3 = set(['C', 'D',' E', 'F', 'G'])
+
+    venn3([set1, set2, set3], ('Set1', 'Set2', 'Set3'))
+    plt.show()
+
+
+Questions
+---------
+* If you ask your questions at `StackOverflow <http://stackoverflow.com/>`_ and tag them `matplotlib-venn <http://stackoverflow.com/questions/tagged/matplotlib-venn>`_, chances are high you'll get an answer from the maintainer of this package.
+
+
+See also
+--------
+
+* Report issues and submit fixes at Github:
+  https://github.com/konstantint/matplotlib-venn
+  
+  Check out the ``DEVELOPER-README.rst`` for development-related notes.
+* Some alternative means of plotting a Venn diagram (as of
+  October 2012) are reviewed in the blog post:
+  http://fouryears.eu/2012/10/13/venn-diagrams-in-python/
+* The `matplotlib-subsets
+  <https://pypi.python.org/pypi/matplotlib-subsets>`_ package
+  visualizes a hierarchy of sets as a tree of rectangles.
diff --git a/matplotlib_venn.egg-info/PKG-INFO b/matplotlib_venn.egg-info/PKG-INFO
new file mode 100644
index 0000000000000000000000000000000000000000..d2e184cbf1086d60a82f72a92f70f75896578acb
--- /dev/null
+++ b/matplotlib_venn.egg-info/PKG-INFO
@@ -0,0 +1,154 @@
+Metadata-Version: 1.1
+Name: matplotlib-venn
+Version: 0.11.5
+Summary: Functions for plotting area-proportional two- and three-way Venn diagrams in matplotlib.
+Home-page: https://github.com/konstantint/matplotlib-venn
+Author: Konstantin Tretyakov
+Author-email: kt@ut.ee
+License: MIT
+Description: ====================================================
+        Venn diagram plotting routines for Python/Matplotlib
+        ====================================================
+        
+        .. image::  https://travis-ci.org/konstantint/matplotlib-venn.png?branch=master
+           :target: https://travis-ci.org/konstantint/matplotlib-venn
+        
+        Routines for plotting area-weighted two- and three-circle venn diagrams.
+        
+        Installation
+        ------------
+        
+        The simplest way to install the package is via ``easy_install`` or
+        ``pip``::
+        
+            $ easy_install matplotlib-venn
+        
+        Dependencies
+        ------------
+        
+        - ``numpy``,
+        - ``scipy``,
+        - ``matplotlib``.
+        
+        Usage
+        -----
+        The package provides four main functions: ``venn2``,
+        ``venn2_circles``, ``venn3`` and ``venn3_circles``.
+        
+        The functions ``venn2`` and ``venn2_circles`` accept as their only
+        required argument a 3-element list ``(Ab, aB, AB)`` of subset sizes,
+        e.g.::
+        
+            venn2(subsets = (3, 2, 1))
+        
+        and draw a two-circle venn diagram with respective region areas. In
+        the particular example, the region, corresponding to subset ``A and
+        not B`` will be three times larger in area than the region,
+        corresponding to subset ``A and B``. Alternatively, you can simply
+        provide a list of two ``set`` or ``Counter`` (i.e. multi-set) objects instead (new in version 0.7),
+        e.g.::
+        
+            venn2([set(['A', 'B', 'C', 'D']), set(['D', 'E', 'F'])])
+        
+        Similarly, the functions ``venn3`` and ``venn3_circles`` take a
+        7-element list of subset sizes ``(Abc, aBc, ABc, abC, AbC, aBC,
+        ABC)``, and draw a three-circle area-weighted venn
+        diagram. Alternatively, you can provide a list of three ``set`` or ``Counter`` objects
+        (rather than counting sizes for all 7 subsets).
+        
+        The functions ``venn2_circles`` and ``venn3_circles`` draw just the
+        circles, whereas the functions ``venn2`` and ``venn3`` draw the
+        diagrams as a collection of colored patches, annotated with text
+        labels. In addition (version 0.7+), functions ``venn2_unweighted`` and
+        ``venn3_unweighted`` draw the Venn diagrams without area-weighting.
+        
+        Note that for a three-circle venn diagram it is not in general
+        possible to achieve exact correspondence between the required set
+        sizes and region areas, however in most cases the picture will still
+        provide a decent indication.
+        
+        The functions ``venn2_circles`` and ``venn3_circles`` return the list of ``matplotlib.patch.Circle`` objects that may be tuned further
+        to your liking. The functions ``venn2`` and ``venn3`` return an object of class ``VennDiagram``,
+        which gives access to constituent patches, text elements, and (since
+        version 0.7) the information about the centers and radii of the
+        circles.
+        
+        Basic Example::
+        
+            from matplotlib_venn import venn2
+            venn2(subsets = (3, 2, 1))
+        
+        For the three-circle case::
+        
+            from matplotlib_venn import venn3
+            venn3(subsets = (1, 1, 1, 2, 1, 2, 2), set_labels = ('Set1', 'Set2', 'Set3'))
+        
+        A more elaborate example::
+        
+            from matplotlib import pyplot as plt
+            import numpy as np
+            from matplotlib_venn import venn3, venn3_circles
+            plt.figure(figsize=(4,4))
+            v = venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C'))
+            v.get_patch_by_id('100').set_alpha(1.0)
+            v.get_patch_by_id('100').set_color('white')
+            v.get_label_by_id('100').set_text('Unknown')
+            v.get_label_by_id('A').set_text('Set "A"')
+            c = venn3_circles(subsets=(1, 1, 1, 1, 1, 1, 1), linestyle='dashed')
+            c[0].set_lw(1.0)
+            c[0].set_ls('dotted')
+            plt.title("Sample Venn diagram")
+            plt.annotate('Unknown set', xy=v.get_label_by_id('100').get_position() - np.array([0, 0.05]), xytext=(-70,-70),
+                         ha='center', textcoords='offset points', bbox=dict(boxstyle='round,pad=0.5', fc='gray', alpha=0.1),
+                         arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.5',color='gray'))
+            plt.show()
+        
+        An example with multiple subplots (new in version 0.6)::
+        
+            from matplotlib_venn import venn2, venn2_circles
+            figure, axes = plt.subplots(2, 2)
+            venn2(subsets={'10': 1, '01': 1, '11': 1}, set_labels = ('A', 'B'), ax=axes[0][0])
+            venn2_circles((1, 2, 3), ax=axes[0][1])
+            venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C'), ax=axes[1][0])
+            venn3_circles({'001': 10, '100': 20, '010': 21, '110': 13, '011': 14}, ax=axes[1][1])
+            plt.show()
+        
+        Perhaps the most common use case is generating a Venn diagram given
+        three sets of objects::
+        
+            set1 = set(['A', 'B', 'C', 'D'])
+            set2 = set(['B', 'C', 'D', 'E'])
+            set3 = set(['C', 'D',' E', 'F', 'G'])
+        
+            venn3([set1, set2, set3], ('Set1', 'Set2', 'Set3'))
+            plt.show()
+        
+        
+        Questions
+        ---------
+        * If you ask your questions at `StackOverflow <http://stackoverflow.com/>`_ and tag them `matplotlib-venn <http://stackoverflow.com/questions/tagged/matplotlib-venn>`_, chances are high you'll get an answer from the maintainer of this package.
+        
+        
+        See also
+        --------
+        
+        * Report issues and submit fixes at Github:
+          https://github.com/konstantint/matplotlib-venn
+          
+          Check out the ``DEVELOPER-README.rst`` for development-related notes.
+        * Some alternative means of plotting a Venn diagram (as of
+          October 2012) are reviewed in the blog post:
+          http://fouryears.eu/2012/10/13/venn-diagrams-in-python/
+        * The `matplotlib-subsets
+          <https://pypi.python.org/pypi/matplotlib-subsets>`_ package
+          visualizes a hierarchy of sets as a tree of rectangles.
+        
+Keywords: matplotlib plotting charts venn-diagrams
+Platform: Platform Independent
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Scientific/Engineering :: Visualization
diff --git a/matplotlib_venn.egg-info/SOURCES.txt b/matplotlib_venn.egg-info/SOURCES.txt
new file mode 100644
index 0000000000000000000000000000000000000000..300a5e6642fd8acc05cf954a16b8466590c88b06
--- /dev/null
+++ b/matplotlib_venn.egg-info/SOURCES.txt
@@ -0,0 +1,20 @@
+CHANGELOG.txt
+MANIFEST.in
+README.rst
+setup.cfg
+setup.py
+matplotlib_venn/__init__.py
+matplotlib_venn/_arc.py
+matplotlib_venn/_common.py
+matplotlib_venn/_math.py
+matplotlib_venn/_region.py
+matplotlib_venn/_util.py
+matplotlib_venn/_venn2.py
+matplotlib_venn/_venn3.py
+matplotlib_venn.egg-info/PKG-INFO
+matplotlib_venn.egg-info/SOURCES.txt
+matplotlib_venn.egg-info/dependency_links.txt
+matplotlib_venn.egg-info/pbr.json
+matplotlib_venn.egg-info/requires.txt
+matplotlib_venn.egg-info/top_level.txt
+matplotlib_venn.egg-info/zip-safe
\ No newline at end of file
diff --git a/matplotlib_venn.egg-info/dependency_links.txt b/matplotlib_venn.egg-info/dependency_links.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/matplotlib_venn.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/matplotlib_venn.egg-info/pbr.json b/matplotlib_venn.egg-info/pbr.json
new file mode 100644
index 0000000000000000000000000000000000000000..febbe0f8d2f3185312de7d4331bec75763e13e16
--- /dev/null
+++ b/matplotlib_venn.egg-info/pbr.json
@@ -0,0 +1 @@
+{"is_release": false, "git_version": "e178f43"}
\ No newline at end of file
diff --git a/matplotlib_venn.egg-info/requires.txt b/matplotlib_venn.egg-info/requires.txt
new file mode 100644
index 0000000000000000000000000000000000000000..74fa65e6b53982850383b9f6bf5acf59d2f72e6c
--- /dev/null
+++ b/matplotlib_venn.egg-info/requires.txt
@@ -0,0 +1,3 @@
+matplotlib
+numpy
+scipy
diff --git a/matplotlib_venn.egg-info/top_level.txt b/matplotlib_venn.egg-info/top_level.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3f70aa4b57f9d2fb329e75f1cf1f588cc22bd745
--- /dev/null
+++ b/matplotlib_venn.egg-info/top_level.txt
@@ -0,0 +1 @@
+matplotlib_venn
diff --git a/matplotlib_venn.egg-info/zip-safe b/matplotlib_venn.egg-info/zip-safe
new file mode 100644
index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86
--- /dev/null
+++ b/matplotlib_venn.egg-info/zip-safe
@@ -0,0 +1 @@
+
diff --git a/matplotlib_venn/__init__.py b/matplotlib_venn/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3adaccded899a3751a3db7135f60fe6e54430357
--- /dev/null
+++ b/matplotlib_venn/__init__.py
@@ -0,0 +1,58 @@
+'''
+Venn diagram plotting routines.
+
+Copyright 2012, Konstantin Tretyakov.
+http://kt.era.ee/
+
+Licensed under MIT license.
+
+This package contains routines for plotting area-weighted two- and three-circle venn diagrams.
+There are four main functions here: :code:`venn2`, :code:`venn2_circles`, :code:`venn3`, :code:`venn3_circles`.
+
+The :code:`venn2` and :code:`venn2_circles`  accept as their only required argument a 3-element list of subset sizes:
+
+    subsets = (Ab, aB, AB)
+
+That is, for example, subsets[0] contains the size of the subset (A and not B), and
+subsets[2] contains the size of the set (A and B), etc.
+
+Similarly, the functions :code:`venn3` and :code:`venn3_circles` require a 7-element list:
+
+    subsets = (Abc, aBc, ABc, abC, AbC, aBC, ABC)
+
+The functions :code:`venn2_circles` and :code:`venn3_circles` simply draw two or three circles respectively,
+such that their intersection areas correspond to the desired set intersection sizes.
+Note that for a three-circle venn diagram it is not possible to achieve exact correspondence, although in
+most cases the picture will still provide a decent indication.
+
+The functions :code:`venn2` and :code:`venn3` draw diagram as a collection of separate colored patches with text labels.
+
+The functions :code:`venn2_circles` and :code:`venn3_circles` return the list of Circle patches that may be tuned further
+to your liking.
+
+The functions :code:`venn2` and :code:`venn3` return an object of class :code:`Venn2` or :code:`Venn3` respectively,
+which give access to constituent patches and text elements.
+
+Example::
+
+    from matplotlib import pyplot as plt
+    import numpy as np
+    from matplotlib_venn import venn3, venn3_circles
+    plt.figure(figsize=(4,4))
+    v = venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C'))
+    v.get_patch_by_id('100').set_alpha(1.0)
+    v.get_patch_by_id('100').set_color('white')
+    v.get_label_by_id('100').set_text('Unknown')
+    v.get_label_by_id('A').set_text('Set "A"')
+    c = venn3_circles(subsets=(1, 1, 1, 1, 1, 1, 1), linestyle='dashed')
+    c[0].set_lw(1.0)
+    c[0].set_ls('dotted')
+    plt.title("Sample Venn diagram")
+    plt.annotate('Unknown set', xy=v.get_text_by_id('100').get_position() - np.array([0, 0.05]), xytext=(-70,-70),
+                ha='center', textcoords='offset points', bbox=dict(boxstyle='round,pad=0.5', fc='gray', alpha=0.1),
+                arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.5',color='gray'))
+'''
+from matplotlib_venn._venn2 import venn2, venn2_circles
+from matplotlib_venn._venn3 import venn3, venn3_circles
+from matplotlib_venn._util import venn2_unweighted, venn3_unweighted
+___all___ = ['venn2', 'venn2_circles', 'venn3', 'venn3_circles', 'venn2_unweighted', 'venn3_unweighted']
diff --git a/matplotlib_venn/_arc.py b/matplotlib_venn/_arc.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4d5b9df3d2ff5eb9db99f2f663d40eec78d8f5b
--- /dev/null
+++ b/matplotlib_venn/_arc.py
@@ -0,0 +1,487 @@
+'''
+Venn diagram plotting routines.
+General-purpose math routines for computing with circular arcs.
+Everything is encapsulated in the "Arc" class.
+
+Copyright 2014, Konstantin Tretyakov.
+http://kt.era.ee/
+
+Licensed under MIT license.
+'''
+import numpy as np
+from matplotlib_venn._math import tol, circle_circle_intersection, vector_angle_in_degrees
+
+class Arc(object):
+    '''
+    A representation of a directed circle arc.
+    Essentially it is a namedtuple(center, radius, from_angle, to_angle, direction) with a bunch of helper methods
+    for measuring arc lengths and intersections.
+    
+    The from_angle and to_angle of an arc must be represented in degrees.
+    The direction is a boolean, with True corresponding to counterclockwise (positive) direction, and False - clockwise (negative).
+    For convenience, the class defines a "sign" property, which is +1 if direction = True and -1 otherwise.
+    '''
+    
+    def __init__(self, center, radius, from_angle, to_angle, direction):
+        '''Raises a ValueError if radius is negative.
+        
+        >>> a = Arc((0, 0), -1, 0, 0, True)
+        Traceback (most recent call last):
+        ...
+        ValueError: Arc's radius may not be negative
+        >>> a = Arc((0, 0), 0, 0, 0, True)        
+        >>> a = Arc((0, 0), 1, 0, 0, True)        
+        '''
+        self.center = np.asarray(center)
+        self.radius = float(radius)
+        if radius < 0.0:
+            raise ValueError("Arc's radius may not be negative")
+        self.from_angle = float(from_angle)
+        self.to_angle = float(to_angle)
+        self.direction = direction
+        self.sign = 1 if direction else -1
+        
+    def length_degrees(self):
+        '''Computes the length of the arc in degrees.
+        The length computation corresponds to what you would expect if you would draw the arc using matplotlib taking direction into account.
+        
+        >>> Arc((0,0), 1, 0, 0, True).length_degrees()
+        0.0
+        >>> Arc((0,0), 2, 0, 0, False).length_degrees()
+        0.0
+        
+        >>> Arc((0,0), 3, 0, 1, True).length_degrees()
+        1.0
+        >>> Arc((0,0), 4, 0, 1, False).length_degrees()
+        359.0
+
+        >>> Arc((0,0), 5, 0, 360, True).length_degrees()
+        360.0
+        >>> Arc((0,0), 6, 0, 360, False).length_degrees()
+        0.0
+        
+        >>> Arc((0,0), 7, 0, 361, True).length_degrees()
+        360.0
+        >>> Arc((0,0), 8, 0, 361, False).length_degrees()
+        359.0
+        
+        >>> Arc((0,0), 9, 10, -10, True).length_degrees()
+        340.0
+        >>> Arc((0,0), 10, 10, -10, False).length_degrees()
+        20.0
+        
+        >>> Arc((0,0), 1, 10, 5, True).length_degrees()
+        355.0
+        >>> Arc((0,0), 1, -10, -5, False).length_degrees()
+        355.0
+        >>> Arc((0,0), 1, 180, -180, True).length_degrees()
+        0.0
+        >>> Arc((0,0), 1, 180, -180, False).length_degrees()
+        360.0
+        >>> Arc((0,0), 1, -180, 180, True).length_degrees()
+        360.0
+        >>> Arc((0,0), 1, -180, 180, False).length_degrees()
+        0.0
+        >>> Arc((0,0), 1, 175, -175, True).length_degrees()
+        10.0
+        >>> Arc((0,0), 1, 175, -175, False).length_degrees()
+        350.0
+        '''
+        d_angle = self.sign * (self.to_angle - self.from_angle)
+        if (d_angle > 360):
+            return 360.0
+        elif (d_angle < 0):
+            return d_angle % 360.0
+        else:
+            return abs(d_angle)   # Yes, abs() is needed, otherwise we get the weird "-0.0" output in the doctests
+
+    def length_radians(self):
+        '''Returns the length of the arc in radians.
+        
+        >>> Arc((0,0), 1, 0, 0, True).length_radians()
+        0.0
+        >>> Arc((0,0), 2, 0, 360, True).length_radians()
+        6.283...
+        >>> Arc((0,0), 6, -18, 18, True).length_radians()
+        0.6283...
+        '''
+        return self.length_degrees() * np.pi / 180.0
+    
+    def length(self):
+        '''Returns the actual length of the arc.
+        
+        >>> Arc((0,0), 2, 0, 360, True).length()
+        12.566...
+        >>> Arc((0,0), 2, 90, 360, False).length()
+        3.1415...
+        >>> Arc((0,0), 0, 90, 360, True).length()
+        0.0
+        '''
+        return self.radius * self.length_radians()
+    
+    def sector_area(self):
+        '''Returns the area of the corresponding arc sector.
+        
+        >>> Arc((0,0), 2, 0, 360, True).sector_area()
+        12.566...
+        >>> Arc((0,0), 2, 0, 36, True).sector_area()
+        1.2566...
+        >>> Arc((0,0), 2, 0, 36, False).sector_area()
+        11.3097...
+        '''
+        return self.radius**2 / 2 * self.length_radians()
+    
+    def segment_area(self):
+        '''Returns the area of the corresponding arc segment.
+        
+        >>> Arc((0,0), 2, 0, 360, True).segment_area()
+        12.566...
+        >>> Arc((0,0), 2, 0, 180, True).segment_area()
+        6.283...
+        >>> Arc((0,0), 2, 0, 90, True).segment_area()
+        1.14159...
+        >>> Arc((0,0), 2, 0, 90, False).segment_area()
+        11.42477796...
+        >>> Arc((0,0), 2, 0, 0, False).segment_area()
+        0.0
+        >>> Arc((0, 9), 1, 89.99, 90, False).segment_area()
+        3.1415...
+        '''
+        theta = self.length_radians()
+        return self.radius**2 / 2 * (theta - np.sin(theta))
+
+    def angle_as_point(self, angle):
+        '''
+        Converts a given angle in degrees to the point coordinates on the arc's circle.
+        Inverse of point_to_angle.
+        
+        >>> Arc((1, 1), 1, 0, 0, True).angle_as_point(0)
+        array([ 2.,  1.])
+        >>> Arc((1, 1), 1, 0, 0, True).angle_as_point(90)
+        array([ 1.,  2.])
+        >>> Arc((1, 1), 1, 0, 0, True).angle_as_point(-270)
+        array([ 1.,  2.])
+        '''
+        angle_rad = angle * np.pi / 180.0
+        return self.center + self.radius * np.array([np.cos(angle_rad), np.sin(angle_rad)])
+    
+    def start_point(self):
+        '''
+        Returns a 2x1 numpy array with the coordinates of the arc's start point.
+        
+        >>> Arc((0, 0), 1, 0, 0, True).start_point()
+        array([ 1.,  0.])
+        >>> Arc((0, 0), 1, 45, 0, True).start_point()
+        array([ 0.707...,  0.707...])
+        '''
+        return self.angle_as_point(self.from_angle)
+
+    def end_point(self):
+        '''
+        Returns a 2x1 numpy array with the coordinates of the arc's end point.
+        
+        >>> np.all(Arc((0, 0), 1, 0, 90, True).end_point() - np.array([0, 1]) < tol)
+        True
+        '''
+        return self.angle_as_point(self.to_angle)
+    
+    def mid_point(self):
+        '''
+        Returns the midpoint of the arc as a 1x2 numpy array.
+        '''
+        midpoint_angle = self.from_angle + self.sign*self.length_degrees() / 2
+        return self.angle_as_point(midpoint_angle)
+    
+    def approximately_equal(self, arc, tolerance=tol):
+        '''
+        Returns true if the parameters of this arc are within <tolerance> of the parameters of the other arc, and the direction is the same.
+        Note that no angle simplification is performed (i.e. some arcs that might be equal in principle are not declared as such
+        by this method)
+        
+        >>> Arc((0, 0), 10, 20, 30, True).approximately_equal(Arc((tol/2, tol/2), 10+tol/2, 20-tol/2, 30-tol/2, True))
+        True
+        >>> Arc((0, 0), 10, 20, 30, True).approximately_equal(Arc((0, 0), 10, 20, 30, False))
+        False
+        >>> Arc((0, 0), 10, 20, 30, True).approximately_equal(Arc((0, 0+tol), 10, 20, 30, True))
+        False
+        '''
+        return self.direction == arc.direction \
+                and np.all(abs(self.center - arc.center) < tolerance) and abs(self.radius - arc.radius) < tolerance \
+                and abs(self.from_angle - arc.from_angle) < tolerance and abs(self.to_angle - arc.to_angle) < tolerance
+    
+    def point_as_angle(self, pt):
+        '''
+        Given a point located on the arc's circle, return the corresponding angle in degrees.
+        No check is done that the point lies on the circle
+        (this is essentially a convenience wrapper around _math.vector_angle_in_degrees)
+        
+        >>> a = Arc((0, 0), 1, 0, 0, True)
+        >>> a.point_as_angle((1, 0))
+        0.0
+        >>> a.point_as_angle((1, 1))
+        45.0
+        >>> a.point_as_angle((0, 1))
+        90.0
+        >>> a.point_as_angle((-1, 1))
+        135.0
+        >>> a.point_as_angle((-1, 0))
+        180.0
+        >>> a.point_as_angle((-1, -1))
+        -135.0
+        >>> a.point_as_angle((0, -1))
+        -90.0
+        >>> a.point_as_angle((1, -1))
+        -45.0
+        '''
+        return vector_angle_in_degrees(np.asarray(pt) - self.center)
+    
+    def contains_angle_degrees(self, angle):
+        '''
+        Returns true, if a point with the corresponding angle (given in degrees) is within the arc.
+        Does no tolerance checks (i.e. if the arc is of length 0, you must provide angle == from_angle == to_angle to get a positive answer here)
+        
+        >>> a = Arc((0, 0), 1, 0, 0, True)
+        >>> assert a.contains_angle_degrees(0)
+        >>> assert a.contains_angle_degrees(360)
+        >>> assert not a.contains_angle_degrees(1)
+        
+        >>> a = Arc((0, 0), 1, 170, -170, True)
+        >>> assert not a.contains_angle_degrees(165)
+        >>> assert a.contains_angle_degrees(170)
+        >>> assert a.contains_angle_degrees(175)
+        >>> assert a.contains_angle_degrees(180)
+        >>> assert a.contains_angle_degrees(185)
+        >>> assert a.contains_angle_degrees(190)
+        >>> assert not a.contains_angle_degrees(195)
+        
+        >>> assert not a.contains_angle_degrees(-195)
+        >>> assert a.contains_angle_degrees(-190)
+        >>> assert a.contains_angle_degrees(-185)
+        >>> assert a.contains_angle_degrees(-180)
+        >>> assert a.contains_angle_degrees(-175)
+        >>> assert a.contains_angle_degrees(-170)
+        >>> assert not a.contains_angle_degrees(-165)
+        >>> assert a.contains_angle_degrees(-170 - 360)
+        >>> assert a.contains_angle_degrees(-190 - 360)
+        >>> assert a.contains_angle_degrees(170 + 360)
+        >>> assert not a.contains_angle_degrees(0)
+        >>> assert not a.contains_angle_degrees(100)
+        >>> assert not a.contains_angle_degrees(-100)
+        '''
+        _d = self.sign * (angle - self.from_angle) % 360.0
+        return (_d <= self.length_degrees())
+    
+    def intersect_circle(self, center, radius):
+        '''
+        Given a circle, finds the intersection point(s) of the arc with the circle.
+        Returns a list of 2x1 numpy arrays. The list has length 0, 1 or 2, depending on how many intesection points there are.
+        If the circle touches the arc, it is reported as two intersection points (which are equal).
+        Points are ordered along the arc.
+        Intersection with the same circle as the arc's own (which means infinitely many points usually) is reported as no intersection at all.
+        
+        >>> a = Arc((0, 0), 1, -60, 60, True)
+        >>> a.intersect_circle((1, 0), 1)
+        [array([ 0.5..., -0.866...]), array([ 0.5...,  0.866...])]
+        >>> a.intersect_circle((0.9, 0), 1)
+        []
+        >>> a.intersect_circle((1,-0.1), 1)
+        [array([ 0.586...,  0.810...])]
+        >>> a.intersect_circle((1, 0.1), 1)
+        [array([ 0.586..., -0.810...])]
+        >>> a.intersect_circle((0, 0), 1)  # Infinitely many intersection points
+        []
+        >>> a.intersect_circle((2, 0), 1)  # Touching point, hence repeated twice
+        [array([ 1.,  0.]), array([ 1.,  0.])]
+        
+        >>> a = Arc((0, 0), 1, 60, -60, False) # Same arc, different direction
+        >>> a.intersect_circle((1, 0), 1)
+        [array([ 0.5...,  0.866...]), array([ 0.5..., -0.866...])]
+        
+        >>> a = Arc((0, 0), 1, 120, -120, True)
+        >>> a.intersect_circle((-1, 0), 1)
+        [array([-0.5...,  0.866...]), array([-0.5..., -0.866...])]
+        >>> a.intersect_circle((-0.9, 0), 1)
+        []
+        >>> a.intersect_circle((-1,-0.1), 1)
+        [array([-0.586...,  0.810...])]
+        >>> a.intersect_circle((-1, 0.1), 1)
+        [array([-0.586..., -0.810...])]
+        >>> a.intersect_circle((-2, 0), 1)
+        [array([-1.,  0.]), array([-1.,  0.])]
+        >>> a = Arc((0, 0), 1, -120, 120, False)
+        >>> a.intersect_circle((-1, 0), 1)
+        [array([-0.5..., -0.866...]), array([-0.5...,  0.866...])]
+        '''
+        intersections = circle_circle_intersection(self.center, self.radius, center, radius)
+        if intersections is None:
+            return []
+        
+        # Check whether the points lie on the arc and order them accordingly
+        _len = self.length_degrees()
+        isections = [[self.sign * (self.point_as_angle(pt) - self.from_angle) % 360.0, pt] for pt in intersections]
+        
+        # Try to find as many candidate intersections as possible (i.e. +- tol within arc limits)
+        # Unless arc's length is 360, interpret intersections just before the arc's starting point as belonging to the starting point.
+        if _len < 360.0 - tol:
+            for isec in isections:
+                if isec[0] > 360.0 - tol:
+                    isec[0] = 0.0
+        
+        isections = [(a, pt[0], pt[1]) for (a, pt) in isections if a < _len + tol or a > 360 - tol]
+        isections.sort()
+        return [np.array([b, c]) for (a, b, c) in isections]
+    
+    def intersect_arc(self, arc):
+        '''
+        Given an arc, finds the intersection point(s) of this arc with that.
+        Returns a list of 2x1 numpy arrays. The list has length 0, 1 or 2, depending on how many intesection points there are.
+        Points are ordered along the arc.
+        Intersection with the arc along the same circle (which means infinitely many points usually) is reported as no intersection at all.
+        
+        >>> a = Arc((0, 0), 1, -90, 90, True)
+        >>> a.intersect_arc(Arc((1, 0), 1, 90, 270, True))
+        [array([ 0.5      , -0.866...]), array([ 0.5      ,  0.866...])]
+        >>> a.intersect_arc(Arc((1, 0), 1, 90, 180, True))
+        [array([ 0.5      ,  0.866...])]
+        >>> a.intersect_arc(Arc((1, 0), 1, 121, 239, True))
+        []
+        >>> a.intersect_arc(Arc((1, 0), 1, 120-tol, 240+tol, True))     # Without -tol and +tol the results differ on different architectures due to rounding (see Debian #813782).
+        [array([ 0.5      , -0.866...]), array([ 0.5      ,  0.866...])]
+        '''
+        intersections = self.intersect_circle(arc.center, arc.radius)
+        isections = [pt for pt in intersections if arc.contains_angle_degrees(arc.point_as_angle(pt))]
+        return isections
+    
+    def subarc(self, from_angle=None, to_angle=None):
+        '''
+        Creates a sub-arc from a given angle (or beginning of this arc) to a given angle (or end of this arc).
+        Verifies that from_angle and to_angle are within the arc and properly ordered.
+        If from_angle is None, start of this arc is used instead.
+        If to_angle is None, end of this arc is used instead.
+        Angles are given in degrees.
+        
+        >>> a = Arc((0, 0), 1, 0, 360, True)
+        >>> a.subarc(None, None)
+        Arc([0.000, 0.000], 1.000,   0.000, 360.000,   True,   degrees=360.000)
+        >>> a.subarc(360, None)
+        Arc([0.000, 0.000], 1.000,   360.000, 360.000,   True,   degrees=0.000)
+        >>> a.subarc(0, None)
+        Arc([0.000, 0.000], 1.000,   0.000, 360.000,   True,   degrees=360.000)
+        >>> a.subarc(-10, None)
+        Arc([0.000, 0.000], 1.000,   350.000, 360.000,   True,   degrees=10.000)
+        >>> a.subarc(None, -10)
+        Arc([0.000, 0.000], 1.000,   0.000, 350.000,   True,   degrees=350.000)
+        >>> a.subarc(1, 359).subarc(2, 358).subarc()
+        Arc([0.000, 0.000], 1.000,   2.000, 358.000,   True,   degrees=356.000)
+        '''
+        
+        if from_angle is None:
+            from_angle = self.from_angle
+        if to_angle is None:
+            to_angle = self.to_angle
+        cur_length = self.length_degrees()
+        d_new_from = self.sign * (from_angle - self.from_angle)
+        if (d_new_from != 360.0):
+            d_new_from = d_new_from % 360.0
+        d_new_to = self.sign * (to_angle - self.from_angle)
+        if (d_new_to != 360.0):
+            d_new_to = d_new_to % 360.0
+        # Gracefully handle numeric precision issues for zero-length arcs
+        if abs(d_new_from - d_new_to) < tol:
+            d_new_from = d_new_to
+        if d_new_to < d_new_from:
+            raise ValueError("Subarc to-angle must be smaller than from-angle.")
+        if d_new_to > cur_length + tol:
+            raise ValueError("Subarc to-angle must lie within the current arc.")
+        return Arc(self.center, self.radius, self.from_angle + self.sign*d_new_from, self.from_angle + self.sign*d_new_to, self.direction)
+    
+    def subarc_between_points(self, p_from=None, p_to=None):
+        '''
+        Given two points on the arc, extract a sub-arc between those points.
+        No check is made to verify the points are actually on the arc.
+        It is basically a wrapper around subarc(point_as_angle(p_from), point_as_angle(p_to)).
+        Either p_from or p_to may be None to denote first or last arc endpoints.
+        
+        >>> a = Arc((0, 0), 1, 0, 90, True)
+        >>> a.subarc_between_points((1, 0), (np.cos(np.pi/4), np.sin(np.pi/4)))
+        Arc([0.000, 0.000], 1.000,   0.000, 45.000,   True,   degrees=45.000)
+        >>> a.subarc_between_points(None, None)
+        Arc([0.000, 0.000], 1.000,   0.000, 90.000,   True,   degrees=90.000)
+        >>> a.subarc_between_points((np.cos(np.pi/4), np.sin(np.pi/4)))
+        Arc([0.000, 0.000], 1.000,   45.000, 90.000,   True,   degrees=45.000)
+        '''
+        a_from = self.point_as_angle(p_from) if p_from is not None else None
+        a_to = self.point_as_angle(p_to) if p_to is not None else None
+        return self.subarc(a_from, a_to)
+    
+    def reversed(self):
+        '''
+        Returns a copy of this arc, with the direction flipped.
+        
+        >>> Arc((0, 0), 1, 0, 360, True).reversed()
+        Arc([0.000, 0.000], 1.000,   360.000, 0.000,   False,   degrees=360.000)
+        >>> Arc((0, 0), 1, 175, -175, True).reversed()
+        Arc([0.000, 0.000], 1.000,   -175.000, 175.000,   False,   degrees=10.000)
+        >>> Arc((0, 0), 1, 0, 370, True).reversed()
+        Arc([0.000, 0.000], 1.000,   370.000, 0.000,   False,   degrees=360.000)
+        '''
+        return Arc(self.center, self.radius, self.to_angle, self.from_angle, not self.direction)
+    
+    def direction_vector(self, angle):
+        '''
+        Returns a unit vector, pointing in the arc's movement direction at a given (absolute) angle (in degrees).
+        No check is made whether angle lies within the arc's span (the results for angles outside of the arc's span )
+        Returns a 2x1 numpy array.
+        
+        >>> a = Arc((0, 0), 1, 0, 90, True)
+        >>> assert all(abs(a.direction_vector(0) - np.array([0.0, 1.0])) < tol)
+        >>> assert all(abs(a.direction_vector(45) - np.array([ -0.70710678, 0.70710678])) < 1e-6)
+        >>> assert all(abs(a.direction_vector(90) - np.array([-1.0, 0.0])) < tol)
+        >>> assert all(abs(a.direction_vector(135) - np.array([-0.70710678, -0.70710678])) < 1e-6)
+        >>> assert all(abs(a.direction_vector(-180) - np.array([0.0, -1.0])) < tol)
+        >>> assert all(abs(a.direction_vector(-90) - np.array([1.0, 0.0])) < tol)
+        >>> a = a.reversed()
+        >>> assert all(abs(a.direction_vector(0) - np.array([0.0, -1.0])) < tol)
+        >>> assert all(abs(a.direction_vector(45) - np.array([ 0.70710678, -0.70710678])) < 1e-6)
+        >>> assert all(abs(a.direction_vector(90) - np.array([1.0, 0.0])) < tol)
+        >>> assert all(abs(a.direction_vector(135) - np.array([0.70710678, 0.70710678])) < 1e-6)
+        >>> assert all(abs(a.direction_vector(-180) - np.array([0.0, 1.0])) < tol)
+        >>> assert all(abs(a.direction_vector(-90) - np.array([-1.0, 0.0])) < tol)
+        '''
+        a = angle + self.sign * 90
+        a = a * np.pi / 180.0
+        return np.array([np.cos(a), np.sin(a)])
+    
+    def fix_360_to_0(self):
+        '''
+        Sometimes we have to create an arc using from_angle and to_angle computed numerically.
+        If from_angle == to_angle, it may sometimes happen that a tiny discrepancy will make from_angle > to_angle, and instead of
+        getting a 0-length arc we end up with a 360-degree arc.
+        Sometimes we know for sure that a 360-degree arc is not what we want, and in those cases
+        the problem is easy to fix. This helper method does that. It checks whether from_angle and to_angle are numerically similar,
+        and if so makes them equal.
+        
+        >>> a = Arc((0, 0), 1, 0, -tol/2, True)
+        >>> a
+        Arc([0.000, 0.000], 1.000,   0.000, -0.000,   True,   degrees=360.000)
+        >>> a.fix_360_to_0()
+        >>> a
+        Arc([0.000, 0.000], 1.000,   -0.000, -0.000,   True,   degrees=0.000)
+        '''
+        if abs(self.from_angle - self.to_angle) < tol:
+            self.from_angle = self.to_angle
+    
+    def lies_on_circle(self, center, radius):
+        '''Tests whether the arc circle's center and radius match the given ones within <tol> tolerance.
+        
+        >>> a = Arc((0, 0), 1, 0, 0, False)
+        >>> a.lies_on_circle((tol/2, tol/2), 1+tol/2)
+        True
+        >>> a.lies_on_circle((tol/2, tol/2), 1-tol)
+        False
+        '''
+        return np.all(abs(np.asarray(center) - self.center) < tol) and abs(radius - self.radius) < tol
+        
+    def __repr__(self):
+        return "Arc([%0.3f, %0.3f], %0.3f,   %0.3f, %0.3f,   %s,   degrees=%0.3f)" \
+                % (self.center[0], self.center[1], self.radius, self.from_angle, self.to_angle, self.direction, self.length_degrees())
diff --git a/matplotlib_venn/_common.py b/matplotlib_venn/_common.py
new file mode 100644
index 0000000000000000000000000000000000000000..e3460e4afc19fde708d8d03787516d6f78223e93
--- /dev/null
+++ b/matplotlib_venn/_common.py
@@ -0,0 +1,105 @@
+'''
+Venn diagram plotting routines.
+Functionality, common to venn2 and venn3.
+
+Copyright 2012, Konstantin Tretyakov.
+http://kt.era.ee/
+
+Licensed under MIT license.
+'''
+import numpy as np
+
+class VennDiagram:
+    '''
+    A container for a set of patches and patch labels and set labels, which make up the rendered venn diagram.
+    This object is returned by a venn2 or venn3 function call.
+    '''
+    id2idx = {'10': 0, '01': 1, '11': 2,
+              '100': 0, '010': 1, '110': 2, '001': 3, '101': 4, '011': 5, '111': 6, 'A': 0, 'B': 1, 'C': 2}
+
+    def __init__(self, patches, subset_labels, set_labels, centers, radii):
+        self.patches = patches
+        self.subset_labels = subset_labels
+        self.set_labels = set_labels
+        self.centers = centers
+        self.radii = radii
+        
+    def get_patch_by_id(self, id):
+        '''Returns a patch by a "region id". 
+           A region id is a string '10', '01' or '11' for 2-circle diagram or a 
+           string like '001', '010', etc, for 3-circle diagram.'''
+        return self.patches[self.id2idx[id]]
+
+    def get_label_by_id(self, id):
+        '''
+        Returns a subset label by a "region id". 
+        A region id is a string '10', '01' or '11' for 2-circle diagram or a 
+        string like '001', '010', etc, for 3-circle diagram.
+        Alternatively, if the string 'A', 'B'  (or 'C' for 3-circle diagram) is given, the label of the
+        corresponding set is returned (or None).'''
+        if len(id) == 1:
+            return self.set_labels[self.id2idx[id]] if self.set_labels is not None else None
+        else:
+            return self.subset_labels[self.id2idx[id]]
+
+    def get_circle_center(self, id):
+        '''
+        Returns the coordinates of the center of a circle as a numpy array (x,y)
+        id must be 0, 1 or 2 (corresponding to the first, second, or third circle). 
+        This is a getter-only (i.e. changing this value does not affect the diagram)
+        '''
+        return self.centers[id]
+    
+    def get_circle_radius(self, id):
+        '''
+        Returns the radius of circle id (where id is 0, 1 or 2).
+        This is a getter-only (i.e. changing this value does not affect the diagram)
+        '''
+        return self.radii[id]
+
+    def hide_zeroes(self):
+        '''
+        Sometimes it makes sense to hide the labels for subsets whose size is zero.
+        This utility method does this.
+        '''
+        for v in self.subset_labels:
+            if v is not None and v.get_text() == '0':
+                v.set_visible(False)
+
+
+def mix_colors(col1, col2, col3=None):
+    '''
+    Mixes two colors to compute a "mixed" color (for purposes of computing
+    colors of the intersection regions based on the colors of the sets.
+    Note that we do not simply compute averages of given colors as those seem
+    too dark for some default configurations. Thus, we lighten the combination up a bit.
+    
+    Inputs are (up to) three RGB triples of floats 0.0-1.0 given as numpy arrays.
+    
+    >>> mix_colors(np.array([1.0, 0., 0.]), np.array([1.0, 0., 0.])) # doctest: +NORMALIZE_WHITESPACE
+    array([ 1.,  0.,  0.])
+    >>> mix_colors(np.array([1.0, 1., 0.]), np.array([1.0, 0.9, 0.]), np.array([1.0, 0.8, 0.1])) # doctest: +NORMALIZE_WHITESPACE
+    array([ 1. ,  1. , 0.04])    
+    '''
+    if col3 is None:
+        mix_color = 0.7 * (col1 + col2)
+    else:
+        mix_color = 0.4 * (col1 + col2 + col3)
+    mix_color = np.min([mix_color, [1.0, 1.0, 1.0]], 0)    
+    return mix_color
+
+
+def prepare_venn_axes(ax, centers, radii):
+    '''
+    Sets properties of the axis object to suit venn plotting. I.e. hides ticks, makes proper xlim/ylim.
+    '''
+    ax.set_aspect('equal')
+    ax.set_xticks([])
+    ax.set_yticks([])
+    min_x = min([centers[i][0] - radii[i] for i in range(len(radii))])
+    max_x = max([centers[i][0] + radii[i] for i in range(len(radii))])
+    min_y = min([centers[i][1] - radii[i] for i in range(len(radii))])
+    max_y = max([centers[i][1] + radii[i] for i in range(len(radii))])
+    ax.set_xlim([min_x - 0.1, max_x + 0.1])
+    ax.set_ylim([min_y - 0.1, max_y + 0.1])
+    ax.set_axis_off()
\ No newline at end of file
diff --git a/matplotlib_venn/_math.py b/matplotlib_venn/_math.py
new file mode 100644
index 0000000000000000000000000000000000000000..7eb6e869e39966c7affed0f6e11cc433cb5c34f8
--- /dev/null
+++ b/matplotlib_venn/_math.py
@@ -0,0 +1,223 @@
+'''
+Venn diagram plotting routines.
+Math helper functions.
+
+Copyright 2012, Konstantin Tretyakov.
+http://kt.era.ee/
+
+Licensed under MIT license.
+'''
+
+from scipy.optimize import brentq
+import numpy as np
+
+tol = 1e-10
+
+def point_in_circle(pt, center, radius):
+    '''
+    Returns true if a given point is located inside (or on the border) of a circle.
+    
+    >>> point_in_circle((0, 0), (0, 0), 1)
+    True
+    >>> point_in_circle((1, 0), (0, 0), 1)
+    True
+    >>> point_in_circle((1, 1), (0, 0), 1)
+    False
+    '''
+    d = np.linalg.norm(np.asarray(pt) - np.asarray(center))
+    return d <= radius
+
+def box_product(v1, v2):
+    '''Returns a determinant |v1 v2|. The value is equal to the signed area of a parallelogram built on v1 and v2.
+    The value is positive is v2 is to the left of v1.
+    
+    >>> box_product((0.0, 1.0), (0.0, 1.0))
+    0.0
+    >>> box_product((1.0, 0.0), (0.0, 1.0))
+    1.0
+    >>> box_product((0.0, 1.0), (1.0, 0.0))
+    -1.0
+    '''
+    return v1[0]*v2[1] - v1[1]*v2[0]
+
+
+def circle_intersection_area(r, R, d):
+    '''
+    Formula from: http://mathworld.wolfram.com/Circle-CircleIntersection.html
+    Does not make sense for negative r, R or d
+
+    >>> circle_intersection_area(0.0, 0.0, 0.0)
+    0.0
+    >>> circle_intersection_area(1.0, 1.0, 0.0)
+    3.1415...
+    >>> circle_intersection_area(1.0, 1.0, 1.0)
+    1.2283...
+    '''
+    if np.abs(d) < tol:
+        minR = np.min([r, R])
+        return np.pi * minR**2
+    if np.abs(r - 0) < tol or np.abs(R - 0) < tol:
+        return 0.0
+    d2, r2, R2 = float(d**2), float(r**2), float(R**2)
+    arg = (d2 + r2 - R2) / 2 / d / r
+    arg = np.max([np.min([arg, 1.0]), -1.0])  # Even with valid arguments, the above computation may result in things like -1.001
+    A = r2 * np.arccos(arg)
+    arg = (d2 + R2 - r2) / 2 / d / R
+    arg = np.max([np.min([arg, 1.0]), -1.0])
+    B = R2 * np.arccos(arg)
+    arg = (-d + r + R) * (d + r - R) * (d - r + R) * (d + r + R)
+    arg = np.max([arg, 0])
+    C = -0.5 * np.sqrt(arg)
+    return A + B + C
+
+
+def circle_line_intersection(center, r, a, b):
+    '''
+    Computes two intersection points between the circle centered at <center> and radius <r> and a line given by two points a and b.
+    If no intersection exists, or if a==b, None is returned. If one intersection exists, it is repeated in the answer.
+
+    >>> circle_line_intersection(np.array([0.0, 0.0]), 1, np.array([-1.0, 0.0]), np.array([1.0, 0.0]))
+    array([[ 1.,  0.],
+           [-1.,  0.]])
+    >>> abs(np.round(circle_line_intersection(np.array([1.0, 1.0]), np.sqrt(2), np.array([-1.0, 1.0]), np.array([1.0, -1.0])), 6))
+    array([[ 0.,  0.],
+           [ 0.,  0.]])
+    '''
+    s = b - a
+    # Quadratic eqn coefs
+    A = np.linalg.norm(s)**2
+    if abs(A) < tol:
+        return None
+    B = 2 * np.dot(a - center, s)
+    C = np.linalg.norm(a - center)**2 - r**2
+    disc = B**2 - 4 * A * C
+    if disc < 0.0:
+        return None
+    t1 = (-B + np.sqrt(disc)) / 2.0 / A
+    t2 = (-B - np.sqrt(disc)) / 2.0 / A
+    return np.array([a + t1 * s, a + t2 * s])
+
+
+def find_distance_by_area(r, R, a, numeric_correction=0.0001):
+    '''
+    Solves circle_intersection_area(r, R, d) == a for d numerically (analytical solution seems to be too ugly to pursue).
+    Assumes that a < pi * min(r, R)**2, will fail otherwise.
+
+    The numeric correction parameter is used whenever the computed distance is exactly (R - r) (i.e. one circle must be inside another).
+    In this case the result returned is (R-r+correction). This helps later when we position the circles and need to ensure they intersect.
+
+    >>> find_distance_by_area(1, 1, 0, 0.0)
+    2.0
+    >>> round(find_distance_by_area(1, 1, 3.1415, 0.0), 4)
+    0.0
+    >>> d = find_distance_by_area(2, 3, 4, 0.0)
+    >>> d
+    3.37...
+    >>> round(circle_intersection_area(2, 3, d), 10)
+    4.0
+    >>> find_distance_by_area(1, 2, np.pi)
+    1.0001
+    '''
+    if r > R:
+        r, R = R, r
+    if np.abs(a) < tol:
+        return float(r + R)
+    if np.abs(min([r, R])**2 * np.pi - a) < tol:
+        return np.abs(R - r + numeric_correction)
+    return brentq(lambda x: circle_intersection_area(r, R, x) - a, R - r, R + r)
+
+
+def circle_circle_intersection(C_a, r_a, C_b, r_b):
+    '''
+    Finds the coordinates of the intersection points of two circles A and B.
+    Circle center coordinates C_a and C_b, should be given as tuples (or 1x2 arrays).
+    Returns a 2x2 array result with result[0] being the first intersection point (to the right of the vector C_a -> C_b)
+    and result[1] being the second intersection point.
+
+    If there is a single intersection point, it is repeated in output.
+    If there are no intersection points or an infinite number of those, None is returned.
+
+    >>> circle_circle_intersection([0, 0], 1, [1, 0], 1) # Two intersection points
+    array([[ 0.5      , -0.866...],
+           [ 0.5      ,  0.866...]])
+    >>> circle_circle_intersection([0, 0], 1, [2, 0], 1) # Single intersection point (circles touch from outside)
+    array([[ 1.,  0.],
+           [ 1.,  0.]])
+    >>> circle_circle_intersection([0, 0], 1, [0.5, 0], 0.5) # Single intersection point (circles touch from inside)
+    array([[ 1.,  0.],
+           [ 1.,  0.]])
+    >>> circle_circle_intersection([0, 0], 1, [0, 0], 1) is None # Infinite number of intersections (circles coincide)
+    True
+    >>> circle_circle_intersection([0, 0], 1, [0, 0.1], 0.8) is None # No intersections (one circle inside another)
+    True
+    >>> circle_circle_intersection([0, 0], 1, [2.1, 0], 1) is None # No intersections (one circle outside another)
+    True
+    '''
+    C_a, C_b = np.asarray(C_a, float), np.asarray(C_b, float)
+    v_ab = C_b - C_a
+    d_ab = np.linalg.norm(v_ab)
+    if np.abs(d_ab) < tol:  # No intersection points or infinitely many of them (circle centers coincide)
+        return None
+    cos_gamma = (d_ab**2 + r_a**2 - r_b**2) / 2.0 / d_ab / r_a
+    
+    if abs(cos_gamma) > 1.0 + tol/10: # Allow for a tiny numeric tolerance here too (always better to be return something instead of None, if possible)
+        return None         # No intersection point (circles do not touch)
+    if (cos_gamma > 1.0):
+        cos_gamma = 1.0
+    if (cos_gamma < -1.0):
+        cos_gamma = -1.0
+    
+    sin_gamma = np.sqrt(1 - cos_gamma**2)
+    u = v_ab / d_ab
+    v = np.array([-u[1], u[0]])
+    pt1 = C_a + r_a * cos_gamma * u - r_a * sin_gamma * v
+    pt2 = C_a + r_a * cos_gamma * u + r_a * sin_gamma * v
+    return np.array([pt1, pt2])
+
+
+def vector_angle_in_degrees(v):
+    '''
+    Given a vector, returns its elevation angle in degrees (-180..180).
+
+    >>> vector_angle_in_degrees([1, 0])
+    0.0
+    >>> vector_angle_in_degrees([1, 1])
+    45.0
+    >>> vector_angle_in_degrees([0, 1])
+    90.0
+    >>> vector_angle_in_degrees([-1, 1])
+    135.0
+    >>> vector_angle_in_degrees([-1, 0])
+    180.0
+    >>> vector_angle_in_degrees([-1, -1])
+    -135.0
+    >>> vector_angle_in_degrees([0, -1])
+    -90.0
+    >>> vector_angle_in_degrees([1, -1])
+    -45.0
+    '''
+    return np.arctan2(v[1], v[0]) * 180 / np.pi
+
+
+def normalize_by_center_of_mass(coords, radii):
+    '''
+    Given coordinates of circle centers and radii, as two arrays,
+    returns new coordinates array, computed such that the center of mass of the
+    three circles is (0, 0).
+
+    >>> normalize_by_center_of_mass(np.array([[0.0, 0.0], [2.0, 0.0], [1.0, 3.0]]), np.array([1.0, 1.0, 1.0]))
+    array([[-1., -1.],
+           [ 1., -1.],
+           [ 0.,  2.]])
+    >>> normalize_by_center_of_mass(np.array([[0.0, 0.0], [2.0, 0.0], [1.0, 2.0]]), np.array([1.0, 1.0, np.sqrt(2.0)]))
+    array([[-1., -1.],
+           [ 1., -1.],
+           [ 0.,  1.]])
+    '''
+    # Now find the center of mass.
+    radii = radii**2
+    sum_r = np.sum(radii)
+    if sum_r < tol:
+        return coords
+    else:
+        return coords - np.dot(radii, coords) / np.sum(radii)
diff --git a/matplotlib_venn/_region.py b/matplotlib_venn/_region.py
new file mode 100644
index 0000000000000000000000000000000000000000..09dd1bf6a3253ca68a6ed9a2ff723715938e7ce1
--- /dev/null
+++ b/matplotlib_venn/_region.py
@@ -0,0 +1,539 @@
+'''
+Venn diagram plotting routines.
+Math for computing with venn diagram regions.
+
+Copyright 2014, Konstantin Tretyakov.
+http://kt.era.ee/
+
+Licensed under MIT license.
+
+The current logic of drawing the venn diagram is the following:
+ - Position the circles.
+ - Compute the regions of the diagram based on circles
+ - Compute the position of the label within each region.
+ - Create matplotlib PathPatch or Circle objects for each of the regions.
+ 
+This module contains functionality necessary for the second, third and fourth steps of this process.
+
+Note that the regions of an up to 3-circle Venn diagram may be of the following kinds:
+ - No region
+ - A circle
+ - A 2, 3 or 4-arc "poly-arc-gon".  (I.e. a polygon with up to 4 vertices, that are connected by circle arcs)
+ - A set of two 3-arc-gons.
+
+We create each of the regions by starting with a circle, and then either intersecting or subtracting the second and the third circles.
+The classes below implement the region representation, the intersection/subtraction procedures and the conversion to matplotlib patches.
+In addition, each region type has a "label positioning" procedure assigned.
+'''
+import warnings
+import numpy as np
+from matplotlib.patches import Circle, PathPatch, Path
+from matplotlib.path import Path
+from matplotlib_venn._math import tol, circle_circle_intersection, vector_angle_in_degrees
+from matplotlib_venn._math import point_in_circle, box_product
+from matplotlib_venn._arc import Arc
+
+class VennRegionException(Exception):
+    pass
+
+class VennRegion(object):
+    '''
+    This is a superclass of a Venn diagram region, defining the interface that has to be supported by the different region types.
+    '''
+    def subtract_and_intersect_circle(self, center, radius):
+        '''
+        Given a circular region, compute two new regions:
+        one obtained by subtracting the circle from this region, and another obtained by intersecting the circle with the region.
+        
+        In all implementations it is assumed that the circle to be subtracted is not completely within
+        the current region without touching its borders, i.e. it will not form a "hole" when subtracted.
+        
+        Arguments:
+           center (tuple):  A two-element tuple-like, representing the coordinates of the center of the circle.
+           radius (float):  A nonnegative number, the radius of the circle.
+           
+        Returns:
+           a list with two elements - the result of subtracting the circle, and the result of intersecting with the circle.
+        '''
+        raise NotImplementedError("Method not implemented")
+    
+    
+    def label_position(self):
+        '''Compute the position of a label for this region and return it as a 1x2 numpy array (x, y).
+        May return None if label is not applicable.'''
+        raise NotImplementedError("Method not implemented")
+
+    def size(self):
+        '''Return a number, representing the size of the region. It is not important that the number would be a precise
+        measurement, as long as sizes of various regions can be compared to choose the largest one.'''
+        raise NotImplementedError("Method not implemented")
+    
+    def make_patch(self):
+        '''Create a matplotlib patch object, corresponding to this region. May return None if no patch has to be created.'''
+        raise NotImplementedError("Method not implemented")
+
+    def verify(self):
+        '''Self-verification routine for purposes of testing. Raises a VennRegionException if some inconsistencies of internal representation
+        are discovered.'''
+        raise NotImplementedError("Method not implemented")
+    
+class VennEmptyRegion(VennRegion):
+    '''
+    An empty region. To save some memory, returns [self, self] on the subtract_and_intersect_circle operation.
+    It is possible to create an empty region with a non-None label position, by providing it in the constructor.
+    
+    >>> v = VennEmptyRegion()
+    >>> [a, b] = v.subtract_and_intersect_circle((1,2), 3)
+    >>> assert a == v and b == v
+    >>> assert v.label_position() is None
+    >>> assert v.size() == 0
+    >>> assert v.make_patch() is None
+    >>> assert v.is_empty()
+    >>> v = VennEmptyRegion((0, 0))
+    >>> v.label_position()
+    array([ 0.,  0.])
+    '''
+    def __init__(self, label_pos = None):
+        self.label_pos = None if label_pos is None else np.asarray(label_pos, float)
+    def subtract_and_intersect_circle(self, center, radius):
+        return [self, self]
+    def size(self):
+        return 0
+    def label_position(self):
+        return self.label_pos
+    def make_patch(self):
+        return None
+    def is_empty(self):  # We use this in tests as an equivalent of isinstance(VennEmptyRegion)
+        return True
+    def verify(self):
+        pass
+
+class VennCircleRegion(VennRegion):
+    '''
+    A circle-shaped region.
+    
+    >>> vcr = VennCircleRegion((0, 0), 1)
+    >>> vcr.size()
+    3.1415...
+    >>> vcr.label_position()
+    array([ 0.,  0.])
+    >>> vcr.make_patch()
+    <matplotlib.patches.Circle object at ...>
+    >>> sr, ir = vcr.subtract_and_intersect_circle((0.5, 0), 1)
+    >>> assert abs(sr.size() + ir.size() - vcr.size()) < tol
+    '''
+    
+    def __init__(self, center, radius):
+        self.center = np.asarray(center, float)
+        self.radius = abs(radius)
+        if (radius < -tol):
+            raise VennRegionException("Circle with a negative radius is invalid")
+    
+    def subtract_and_intersect_circle(self, center, radius):
+        '''Will throw a VennRegionException if the circle to be subtracted is completely inside and not touching the given region.'''
+        
+        # Check whether the target circle intersects us
+        center = np.asarray(center, float)
+        d = np.linalg.norm(center - self.center)
+        if d > (radius + self.radius - tol):
+            return [self, VennEmptyRegion()] # The circle does not intersect us
+        elif d < tol:
+            if radius > self.radius - tol:
+                # We are completely covered by that circle or we are the same circle
+                return [VennEmptyRegion(), self]
+            else:
+                # That other circle is inside us and smaller than us - we can't deal with it
+                raise VennRegionException("Invalid configuration of circular regions (holes are not supported).")
+        else:
+            # We *must* intersect the other circle. If it is not the case, then it is inside us completely,
+            # and we'll complain.            
+            intersections = circle_circle_intersection(self.center, self.radius, center, radius)
+            
+            if intersections is None:
+                raise VennRegionException("Invalid configuration of circular regions (holes are not supported).")
+            elif np.all(abs(intersections[0] - intersections[1]) < tol) and self.radius < radius:
+                # There is a single intersection point (i.e. we are touching the circle),
+                # the circle to be subtracted is not outside of us (this was checked before), and is larger than us.
+                # This is a particular corner case that is not dealt with correctly by the general-purpose code below and must
+                # be handled separately
+                return [VennEmptyRegion(), self]
+            else:
+                # Otherwise the subtracted region is a 2-arc-gon
+                # Before we need to convert the intersection points as angles wrt each circle.
+                a_1 = vector_angle_in_degrees(intersections[0] - self.center)
+                a_2 = vector_angle_in_degrees(intersections[1] - self.center)
+                b_1 = vector_angle_in_degrees(intersections[0] - center)
+                b_2 = vector_angle_in_degrees(intersections[1] - center)
+
+                # We must take care of the situation where the intersection points happen to be the same
+                if (abs(b_1 - b_2) < tol):
+                    b_1 = b_2 - tol/2
+                if (abs(a_1 - a_2) < tol):
+                    a_2 = a_1 + tol/2
+                
+                # The subtraction is a 2-arc-gon [(AB, B-), (BA, A+)]
+                s_arc1 = Arc(center, radius, b_1, b_2, False)
+                s_arc2 = Arc(self.center, self.radius, a_2, a_1, True)                
+                subtraction = VennArcgonRegion([s_arc1, s_arc2])
+                
+                # .. and the intersection is a 2-arc-gon [(AB, A+), (BA, B+)]
+                i_arc1 = Arc(self.center, self.radius, a_1, a_2, True)
+                i_arc2 = Arc(center, radius, b_2, b_1, True)
+                intersection = VennArcgonRegion([i_arc1, i_arc2])
+                return [subtraction, intersection]
+    
+    def size(self):
+        '''
+        Return the area of the circle
+        
+        >>> VennCircleRegion((0, 0), 1).size()
+        3.1415...
+        >>> VennCircleRegion((0, 0), 2).size()
+        12.56637...
+        '''
+        return np.pi * self.radius**2;
+    
+    def label_position(self):
+        '''
+        The label should be positioned in the center of the circle
+        
+        >>> VennCircleRegion((0, 0), 1).label_position()
+        array([ 0.,  0.])
+        >>> VennCircleRegion((-1.2, 3.4), 1).label_position()
+        array([-1.2,  3.4])
+        '''
+        return self.center
+    
+    def make_patch(self):
+        '''
+        Returns the corresponding circular patch.
+        
+        >>> patch = VennCircleRegion((1, 2), 3).make_patch()
+        >>> patch
+        <matplotlib.patches.Circle object at ...>
+        >>> patch.center, patch.radius
+        (array([ 1.,  2.]), 3.0)
+        '''
+        return Circle(self.center, self.radius)
+
+    def verify(self):
+        pass
+    
+
+class VennArcgonRegion(VennRegion):
+    '''
+    A poly-arc region.
+    Note that we essentially only support 2, 3 and 4 arced regions,
+    whereas intersections and subtractions only work for 2-arc regions.
+    '''
+    
+    def __init__(self, arcs):
+        '''
+        Create a poly-arc region given a list of Arc objects.        
+        The arcs list must be of length 2, 3 or 4.
+        The arcs must form a closed polygon, i.e. the last point of each arc must be the first point of the next arc.
+        The vertices of a 3 or 4-arcgon must be listed in a CCW order. Arcs must not intersect.
+        
+        This is not verified in the constructor, but a special verify() method can be used to check
+        for validity.
+        '''
+        self.arcs = arcs
+        
+    def verify(self):
+        '''
+        Verify the correctness of the region arcs. Throws an VennRegionException if verification fails
+        (or any other exception if it happens during verification).
+        '''
+        # Verify size of arcs list
+        if (len(self.arcs) < 2):
+            raise VennRegionException("At least two arcs needed in a poly-arc region")
+        if (len(self.arcs) > 4):
+            raise VennRegionException("At most 4 arcs are supported currently for poly-arc regions")
+        
+        TRIG_TOL = 100*tol  # We need to use looser tolerance level here because conversion to angles and back is prone to large errors.
+        # Verify connectedness of arcs
+        for i in range(len(self.arcs)):
+            if not np.all(self.arcs[i-1].end_point() - self.arcs[i].start_point() < TRIG_TOL):
+                raise VennRegionException("Arcs of an poly-arc-gon must be connected via endpoints")
+        
+        # Verify that arcs do not cross-intersect except at endpoints
+        for i in range(len(self.arcs)-1):
+            for j in range(i+1, len(self.arcs)):
+                ips = self.arcs[i].intersect_arc(self.arcs[j])
+                for ip in ips:
+                    if not (np.all(abs(ip - self.arcs[i].start_point()) < TRIG_TOL) or np.all(abs(ip - self.arcs[i].end_point()) < TRIG_TOL)):
+                        raise VennRegionException("Arcs of a poly-arc-gon may only intersect at endpoints")
+                
+                if len(ips) != 0 and (i - j) % len(self.arcs) > 1 and (j - i) % len(self.arcs) > 1:
+                    # Two non-consecutive arcs intersect. This is in general not good, but
+                    # may occasionally happen when all arcs inbetween have length 0.
+                    pass # raise VennRegionException("Non-consecutive arcs of a poly-arc-gon may not intersect")
+        
+        # Verify that vertices are ordered so that at each point the direction along the polyarc changes towards the left.
+        # Note that this test only makes sense for polyarcs obtained using circle intersections & subtractions.
+        # A "flower-like" polyarc may have its vertices ordered counter-clockwise yet the direction would turn to the right at each of them.
+        for i in range(len(self.arcs)):
+            prev_arc = self.arcs[i-1]
+            cur_arc = self.arcs[i]
+            if box_product(prev_arc.direction_vector(prev_arc.to_angle), cur_arc.direction_vector(cur_arc.from_angle)) < -tol:
+                raise VennRegionException("Arcs must be ordered so that the direction at each vertex changes counter-clockwise")
+        
+    def subtract_and_intersect_circle(self, center, radius):
+        '''
+        Circle subtraction / intersection only supported by 2-gon regions, otherwise a VennRegionException is thrown.
+        In addition, such an exception will be thrown if the circle to be subtracted is completely within the region and forms a "hole".
+        
+        The result may be either a VennArcgonRegion or a VennMultipieceRegion (the latter happens when the circle "splits" a crescent in two).
+        '''
+        if len(self.arcs) != 2:
+            raise VennRegionException("Circle subtraction and intersection with poly-arc regions is currently only supported for 2-arc-gons.")
+        
+        # In the following we consider the 2-arc-gon case.
+        # Before we do anything, we check for a special case, where the circle of interest is one of the two circles forming the arcs.
+        # In this case we can determine the answer quite easily.
+        matching_arcs = [a for a in self.arcs if a.lies_on_circle(center, radius)]
+        if len(matching_arcs) != 0:
+            # If the circle matches a positive arc, the result is [empty, self], otherwise [self, empty]
+            return [VennEmptyRegion(), self] if matching_arcs[0].direction else [self, VennEmptyRegion()]
+            
+        # Consider the intersection points of the circle with the arcs.
+        # If any of the intersection points corresponds exactly to any of the arc's endpoints, we will end up with
+        # a lot of messy special cases (as if the usual situation is not messy enough, eh).
+        # To avoid that, we cheat by slightly increasing the circle's radius until this is not the case any more.
+        center = np.asarray(center)
+        illegal_intersections = [a.start_point() for a in self.arcs]
+        while True:
+            valid = True
+            intersections = [a.intersect_circle(center, radius) for a in self.arcs]
+            for ints in intersections:
+                for pt in ints:
+                    for illegal_pt in illegal_intersections:
+                        if np.all(abs(pt - illegal_pt) < tol):
+                            valid = False
+            if valid:
+                break
+            else:
+                radius += tol
+                
+
+        # There must be an even number of those points in total.
+        # (If this is not the case, then we have an unfortunate case with weird numeric errors [TODO: find examples and deal with it?]).
+        # There are three possibilities with the following subcases:
+        #   I. No intersection points
+        #       a) The polyarc is completely within the circle.
+        #           result = [ empty, self ]
+        #       b) The polyarc is completely outside the circle.
+        #           result = [ self, empty ]
+        #   II. Four intersection points, two for each arc. Points x1, x2 for arc X and y1, y2 for arc Y, ordered along the arc.
+        #       a) The polyarc endpoints are both outside the circle.
+        #           result_subtraction = a combination of two 3-arc polyarcs:
+        #               1: {X - start to x1,
+        #                   x1 to y2 along circle (negative direction)),
+        #                   Y - y2 to end}
+        #               2: {Y start to y1,
+        #                   y1 to x2 along circle (negative direction)),
+        #                   X - x2 to end}
+        #       b) The polyarc endpoints are both inside the circle
+        #               same as above, but the "along circle" arc directions are flipped and subtraction/intersection parts are exchanged
+        #   III. Two intersection points
+        #       a) One arc, X, has two intersection points i & j, another arc, Y, has no intersection points
+        #           a.1) Polyarc endpoints are outside the circle
+        #               result_subtraction = {X from start to i, circle i to j (direction = negative), X j to end, Y}
+        #               result_intersection = {X i to j, circle j to i (direction = positive}
+        #           a.2) Polyarc endpoints are inside the circle
+        #               result_subtraction = {X i to j, circle j to i negative}
+        #               result_intersection = {X 0 to i, circle i to j positive, X j to end, Y}
+        #       b) Both arcs, X and Y, have one intersection point each. In this case one of the arc endpoints must be inside circle, another outside.
+        #          call the arc that starts with the outside point X, the other arc Y.
+        #           result_subtraction = {X start to intersection, intersection to intersection along circle (negative direction), Y from intersection to end}
+        #           result_intersection = {X intersection to end, Y start to intersecton, intersection to intersecion along circle (positive)}
+        center = np.asarray(center)
+        intersections = [a.intersect_circle(center, radius) for a in self.arcs]
+        
+        if len(intersections[0]) == 0 and len(intersections[1]) == 0:
+            # Case I
+            if point_in_circle(self.arcs[0].start_point(), center, radius):
+                # Case I.a)
+                return [VennEmptyRegion(), self]
+            else:
+                # Case I.b)
+                return [self, VennEmptyRegion()]
+        elif len(intersections[0]) == 2 and len(intersections[1]) == 2:
+            # Case II. a) or b)
+            case_II_a = not point_in_circle(self.arcs[0].start_point(), center, radius)
+            
+            a1 = self.arcs[0].subarc_between_points(None, intersections[0][0])
+            a2 = Arc(center, radius,
+                     vector_angle_in_degrees(intersections[0][0] - center),
+                     vector_angle_in_degrees(intersections[1][1] - center),
+                     not case_II_a)
+            a2.fix_360_to_0()
+            a3 = self.arcs[1].subarc_between_points(intersections[1][1], None)
+            piece1 = VennArcgonRegion([a1, a2, a3])
+            
+            b1 = self.arcs[1].subarc_between_points(None, intersections[1][0])
+            b2 = Arc(center, radius,
+                     vector_angle_in_degrees(intersections[1][0] - center),
+                     vector_angle_in_degrees(intersections[0][1] - center),
+                     not case_II_a)
+            b2.fix_360_to_0()
+            b3 = self.arcs[0].subarc_between_points(intersections[0][1], None)
+            piece2 = VennArcgonRegion([b1, b2, b3])
+            
+            subtraction = VennMultipieceRegion([piece1, piece2])
+            
+            c1 = self.arcs[0].subarc(a1.to_angle, b3.from_angle)
+            c2 = b2.reversed()
+            c3 = self.arcs[1].subarc(b1.to_angle, a3.from_angle)
+            c4 = a2.reversed()
+            intersection = VennArcgonRegion([c1, c2, c3, c4])
+            
+            return [subtraction, intersection] if case_II_a else [intersection, subtraction]
+        else:
+            # Case III. Yuck.
+            if len(intersections[0]) == 0 or len(intersections[1]) == 0:
+                # Case III.a)
+                x = 0 if len(intersections[0]) != 0 else 1
+                y = 1 - x
+                if len(intersections[x]) != 2:
+                    warnings.warn("Numeric precision error during polyarc intersection, case IIIa. Expect wrong results.")
+                    intersections[x] = [intersections[x][0], intersections[x][0]]  # This way we'll at least produce some result, although it will probably be wrong
+                if not point_in_circle(self.arcs[0].start_point(), center, radius):
+                    # Case III.a.1)
+                    #   result_subtraction = {X from start to i, circle i to j (direction = negative), X j to end, Y}
+                    a1 = self.arcs[x].subarc_between_points(None, intersections[x][0])
+                    a2 = Arc(center, radius,
+                             vector_angle_in_degrees(intersections[x][0] - center),
+                             vector_angle_in_degrees(intersections[x][1] - center),
+                             False)
+                    a3 = self.arcs[x].subarc_between_points(intersections[x][1], None)
+                    a4 = self.arcs[y]
+                    subtraction = VennArcgonRegion([a1, a2, a3, a4])
+                    
+                    #   result_intersection = {X i to j, circle j to i (direction = positive)}
+                    b1 = self.arcs[x].subarc(a1.to_angle, a3.from_angle)
+                    b2 = a2.reversed()
+                    intersection = VennArcgonRegion([b1, b2])
+                    
+                    return [subtraction, intersection]
+                else:
+                    # Case III.a.2)
+                    #   result_subtraction = {X i to j, circle j to i negative}
+                    a1 = self.arcs[x].subarc_between_points(intersections[x][0], intersections[x][1])
+                    a2 = Arc(center, radius,
+                             vector_angle_in_degrees(intersections[x][1] - center),
+                             vector_angle_in_degrees(intersections[x][0] - center),
+                             False)
+                    subtraction = VennArcgonRegion([a1, a2])
+                    
+                    #   result_intersection = {X 0 to i, circle i to j positive, X j to end, Y}
+                    b1 = self.arcs[x].subarc(None, a1.from_angle)
+                    b2 = a2.reversed()
+                    b3 = self.arcs[x].subarc(a1.to_angle, None)
+                    b4 = self.arcs[y]
+                    intersection = VennArcgonRegion([b1, b2, b3, b4])
+                    
+                    return [subtraction, intersection]
+            else:
+                # Case III.b)
+                if len(intersections[0]) == 2 or len(intersections[1]) == 2:
+                    warnings.warn("Numeric precision error during polyarc intersection, case IIIb. Expect wrong results.")
+                
+                # One of the arcs must start outside the circle, call it x
+                x = 0 if not point_in_circle(self.arcs[0].start_point(), center, radius) else 1
+                y = 1 - x
+                
+                a1 = self.arcs[x].subarc_between_points(None, intersections[x][0])
+                a2 = Arc(center, radius,
+                         vector_angle_in_degrees(intersections[x][0] - center),
+                         vector_angle_in_degrees(intersections[y][0] - center), False)
+                a3 = self.arcs[y].subarc_between_points(intersections[y][0], None)
+                subtraction = VennArcgonRegion([a1, a2, a3])
+                
+                b1 = self.arcs[x].subarc(a1.to_angle, None)
+                b2 = self.arcs[y].subarc(None, a3.from_angle)
+                b3 = a2.reversed()
+                intersection = VennArcgonRegion([b1, b2, b3])
+                return [subtraction, intersection]
+    
+    def label_position(self):
+        # Position the label right inbetween the midpoints of the arcs
+        midpoints = [a.mid_point() for a in self.arcs]
+        # For two-arc regions take the usual average
+        # For more than two arcs, use arc lengths as the weights.
+        if len(self.arcs) == 2:
+            return np.mean(midpoints, 0)
+        else:
+            lengths = [a.length_degrees() for a in self.arcs]
+            avg = np.sum([mp * l for (mp, l) in zip(midpoints, lengths)], 0)
+            return avg / np.sum(lengths)
+    
+    def size(self):
+        '''Return the area of the patch.
+        
+        The area can be computed using the standard polygon area formula + signed segment areas of each arc.
+        '''
+        polygon_area = 0
+        for a in self.arcs:
+            polygon_area += box_product(a.start_point(), a.end_point())
+        polygon_area /= 2.0
+        return polygon_area + sum([a.sign * a.segment_area() for a in self.arcs])
+    
+    def make_patch(self):
+        '''
+        Retuns a matplotlib PathPatch representing the current region.
+        '''
+        path = [self.arcs[0].start_point()]
+        for a in self.arcs:
+            if a.direction:
+                vertices = Path.arc(a.from_angle, a.to_angle).vertices
+            else:
+                vertices = Path.arc(a.to_angle, a.from_angle).vertices
+                vertices = vertices[np.arange(len(vertices) - 1, -1, -1)]
+            vertices = vertices * a.radius + a.center
+            path = path + list(vertices[1:])
+        codes = [1] + [4] * (len(path) - 1)  # NB: We could also add a CLOSEPOLY code (and a random vertex) to the end
+        return PathPatch(Path(path, codes))
+
+
+class VennMultipieceRegion(VennRegion):
+    '''
+    A region containing several pieces.
+    In principle, any number of pieces is supported,
+    although no more than 2 should ever be needed in a 3-circle Venn diagram.
+    Although subtraction/intersection are straightforward to implement we do
+    not need those for matplotlib-venn, we raise exceptions in those methods.
+    '''
+    
+    def __init__(self, pieces):
+        '''
+        Create a multi-piece region from a list of VennRegion objects.
+        The list may be empty or contain a single item (although those regions can be converted to a
+        VennEmptyRegion or a single region of the necessary type.
+        '''
+        self.pieces = pieces
+        
+    def label_position(self):
+        '''
+        Find the largest region and position the label in that.
+        '''
+        reg_sizes = [(r.size(), r) for r in self.pieces]
+        reg_sizes.sort()
+        return reg_sizes[-1][1].label_position()
+    
+    def size(self):
+        return sum([p.size() for p in self.pieces])
+    
+    def make_patch(self):
+        '''Currently only works if all the pieces are Arcgons.
+           In this case returns a multiple-piece path. Otherwise throws an exception.'''
+        paths = [p.make_patch().get_path() for p in self.pieces]
+        vertices = np.concatenate([p.vertices for p in paths])
+        codes = np.concatenate([p.codes for p in paths])
+        return PathPatch(Path(vertices, codes))
+    
+    def verify(self):
+        for p in self.pieces:
+            p.verify()
+        
+    
diff --git a/matplotlib_venn/_util.py b/matplotlib_venn/_util.py
new file mode 100644
index 0000000000000000000000000000000000000000..7680ca1f1c61547f9a7f2c43310b838919575202
--- /dev/null
+++ b/matplotlib_venn/_util.py
@@ -0,0 +1,63 @@
+'''
+Venn diagram plotting routines.
+Utility routines
+
+Copyright 2012, Konstantin Tretyakov.
+http://kt.era.ee/
+
+Licensed under MIT license.
+'''
+from matplotlib_venn._venn2 import venn2, compute_venn2_subsets
+from matplotlib_venn._venn3 import venn3, compute_venn3_subsets
+
+
+def venn2_unweighted(subsets, set_labels=('A', 'B'), set_colors=('r', 'g'), alpha=0.4, normalize_to=1.0, subset_areas=(1, 1, 1), ax=None, subset_label_formatter=None):
+    '''
+    The version of venn2 without area-weighting.
+    It is implemented as a wrapper around venn2. Namely, venn2 is invoked as usual, but with all subset areas
+    set to 1. The subset labels are then replaced in the resulting diagram with the provided subset sizes.
+    
+    The parameters are all the same as that of venn2.
+    In addition there is a subset_areas parameter, which specifies the actual subset areas.
+    (it is (1, 1, 1) by default. You are free to change it, within reason).
+    '''
+    v = venn2(subset_areas, set_labels, set_colors, alpha, normalize_to, ax)
+    # Now rename the labels
+    if subset_label_formatter is None:
+        subset_label_formatter = str    
+    subset_ids = ['10', '01', '11']
+    if isinstance(subsets, dict):
+        subsets = [subsets.get(t, 0) for t in subset_ids]
+    elif len(subsets) == 2:
+        subsets = compute_venn2_subsets(*subsets)
+    for n, id in enumerate(subset_ids):
+        lbl = v.get_label_by_id(id)
+        if lbl is not None:
+            lbl.set_text(subset_label_formatter(subsets[n]))
+    return v
+
+
+def venn3_unweighted(subsets, set_labels=('A', 'B', 'C'), set_colors=('r', 'g', 'b'), alpha=0.4, normalize_to=1.0, subset_areas=(1, 1, 1, 1, 1, 1, 1), ax=None, subset_label_formatter=None):
+    '''
+    The version of venn3 without area-weighting.
+    It is implemented as a wrapper around venn3. Namely, venn3 is invoked as usual, but with all subset areas
+    set to 1. The subset labels are then replaced in the resulting diagram with the provided subset sizes.
+    
+    The parameters are all the same as that of venn2.
+    In addition there is a subset_areas parameter, which specifies the actual subset areas.
+    (it is (1, 1, 1, 1, 1, 1, 1) by default. You are free to change it, within reason).
+    '''
+    v = venn3(subset_areas, set_labels, set_colors, alpha, normalize_to, ax)
+    # Now rename the labels
+    if subset_label_formatter is None:
+        subset_label_formatter = str    
+    subset_ids = ['100', '010', '110', '001', '101', '011', '111']
+    if isinstance(subsets, dict):
+        subsets = [subsets.get(t, 0) for t in subset_ids]
+    elif len(subsets) == 3:
+        subsets = compute_venn3_subsets(*subsets)
+    for n, id in enumerate(subset_ids):
+        lbl = v.get_label_by_id(id)
+        if lbl is not None:
+            lbl.set_text(subset_label_formatter(subsets[n]))
+    return v
\ No newline at end of file
diff --git a/matplotlib_venn/_venn2.py b/matplotlib_venn/_venn2.py
new file mode 100644
index 0000000000000000000000000000000000000000..e7d696aed0cf2c76e04cf8f3df559d1da917c7c8
--- /dev/null
+++ b/matplotlib_venn/_venn2.py
@@ -0,0 +1,258 @@
+'''
+Venn diagram plotting routines.
+Two-circle venn plotter.
+
+Copyright 2012, Konstantin Tretyakov.
+http://kt.era.ee/
+
+Licensed under MIT license.
+'''
+# Make sure we don't try to do GUI stuff when running tests
+import sys, os
+if 'py.test' in os.path.basename(sys.argv[0]): # (XXX: Ugly hack)
+    import matplotlib
+    matplotlib.use('Agg')
+
+import numpy as np
+import warnings
+from collections import Counter
+
+from matplotlib.patches import Circle
+from matplotlib.colors import ColorConverter
+from matplotlib.pyplot import gca
+
+from matplotlib_venn._math import *
+from matplotlib_venn._common import *
+from matplotlib_venn._region import VennCircleRegion
+
+
+def compute_venn2_areas(diagram_areas, normalize_to=1.0):
+    '''
+    The list of venn areas is given as 3 values, corresponding to venn diagram areas in the following order:
+     (Ab, aB, AB)  (i.e. last element corresponds to the size of intersection A&B&C).
+    The return value is a list of areas (A, B, AB), such that the total area is normalized
+    to normalize_to. If total area was 0, returns (1e-06, 1e-06, 0.0)
+
+    Assumes all input values are nonnegative (to be more precise, all areas are passed through and abs() function)
+    >>> compute_venn2_areas((1, 1, 0))
+    (0.5, 0.5, 0.0)
+    >>> compute_venn2_areas((0, 0, 0))
+    (1e-06, 1e-06, 0.0)
+    >>> compute_venn2_areas((1, 1, 1), normalize_to=3)
+    (2.0, 2.0, 1.0)
+    >>> compute_venn2_areas((1, 2, 3), normalize_to=6)
+    (4.0, 5.0, 3.0)
+    '''
+    # Normalize input values to sum to 1
+    areas = np.array(np.abs(diagram_areas), float)
+    total_area = np.sum(areas)
+    if np.abs(total_area) < tol:
+        warnings.warn("Both circles have zero area")
+        return (1e-06, 1e-06, 0.0)
+    else:
+        areas = areas / total_area * normalize_to
+        return (areas[0] + areas[2], areas[1] + areas[2], areas[2])
+
+
+def solve_venn2_circles(venn_areas):
+    '''
+    Given the list of "venn areas" (as output from compute_venn2_areas, i.e. [A, B, AB]),
+    finds the positions and radii of the two circles.
+    The return value is a tuple (coords, radii), where coords is a 2x2 array of coordinates and
+    radii is a 2x1 array of circle radii.
+
+    Assumes the input values to be nonnegative and not all zero.
+    In particular, the first two values must be positive.
+
+    >>> c, r = solve_venn2_circles((1, 1, 0))
+    >>> np.round(r, 3)
+    array([ 0.564,  0.564])
+    >>> c, r = solve_venn2_circles(compute_venn2_areas((1, 2, 3)))
+    >>> np.round(r, 3)
+    array([ 0.461,  0.515])
+    '''
+    (A_a, A_b, A_ab) = list(map(float, venn_areas))
+    r_a, r_b = np.sqrt(A_a / np.pi), np.sqrt(A_b / np.pi)
+    radii = np.array([r_a, r_b])
+    if A_ab > tol:
+        # Nonzero intersection
+        coords = np.zeros((2, 2))
+        coords[1][0] = find_distance_by_area(radii[0], radii[1], A_ab)
+    else:
+        # Zero intersection
+        coords = np.zeros((2, 2))
+        coords[1][0] = radii[0] + radii[1] + max(np.mean(radii) * 1.1, 0.2)   # The max here is needed for the case r_a = r_b = 0
+    coords = normalize_by_center_of_mass(coords, radii)
+    return (coords, radii)
+
+
+def compute_venn2_regions(centers, radii):
+    '''
+    Returns a triple of VennRegion objects, describing the three regions of the diagram, corresponding to sets
+    (Ab, aB, AB)
+
+    >>> centers, radii = solve_venn2_circles((1, 1, 0.5))
+    >>> regions = compute_venn2_regions(centers, radii)
+    '''
+    A = VennCircleRegion(centers[0], radii[0])
+    B = VennCircleRegion(centers[1], radii[1])
+    Ab, AB = A.subtract_and_intersect_circle(B.center, B.radius)
+    aB, _ = B.subtract_and_intersect_circle(A.center, A.radius)
+    return (Ab, aB, AB)
+
+
+def compute_venn2_colors(set_colors):
+    '''
+    Given two base colors, computes combinations of colors corresponding to all regions of the venn diagram.
+    returns a list of 3 elements, providing colors for regions (10, 01, 11).
+
+    >>> compute_venn2_colors(('r', 'g'))
+    (array([ 1.,  0.,  0.]), array([ 0. ,  0.5,  0. ]), array([ 0.7 ,  0.35,  0.  ]))
+    '''
+    ccv = ColorConverter()
+    base_colors = [np.array(ccv.to_rgb(c)) for c in set_colors]
+    return (base_colors[0], base_colors[1], mix_colors(base_colors[0], base_colors[1]))
+
+
+def compute_venn2_subsets(a, b):
+    '''
+    Given two set or Counter objects, computes the sizes of (a & ~b, b & ~a, a & b).
+    Returns the result as a tuple.
+
+    >>> compute_venn2_subsets(set([1,2,3,4]), set([2,3,4,5,6]))
+    (1, 2, 3)
+    >>> compute_venn2_subsets(Counter([1,2,3,4]), Counter([2,3,4,5,6]))
+    (1, 2, 3)
+    >>> compute_venn2_subsets(Counter([]), Counter([]))
+    (0, 0, 0)
+    >>> compute_venn2_subsets(set([]), set([]))
+    (0, 0, 0)
+    >>> compute_venn2_subsets(set([1]), set([]))
+    (1, 0, 0)
+    >>> compute_venn2_subsets(set([1]), set([1]))
+    (0, 0, 1)
+    >>> compute_venn2_subsets(Counter([1]), Counter([1]))
+    (0, 0, 1)
+    >>> compute_venn2_subsets(set([1,2]), set([1]))
+    (1, 0, 1)
+    >>> compute_venn2_subsets(Counter([1,1,2,2,2]), Counter([1,2,3,3]))
+    (3, 2, 2)
+    >>> compute_venn2_subsets(Counter([1,1,2]), Counter([1,2,2]))
+    (1, 1, 2)
+    >>> compute_venn2_subsets(Counter([1,1]), set([]))
+    Traceback (most recent call last):
+    ...
+    ValueError: Both arguments must be of the same type
+    '''
+    if not (type(a) == type(b)):
+        raise ValueError("Both arguments must be of the same type")
+    set_size = len if type(a) != Counter else lambda x: sum(x.values())   # We cannot use len to compute the cardinality of a Counter
+    return (set_size(a - b), set_size(b - a), set_size(a & b))
+
+
+def venn2_circles(subsets, normalize_to=1.0, alpha=1.0, color='black', linestyle='solid', linewidth=2.0, ax=None, **kwargs):
+    '''
+    Plots only the two circles for the corresponding Venn diagram.
+    Useful for debugging or enhancing the basic venn diagram.
+    parameters ``subsets``, ``normalize_to`` and ``ax`` are the same as in venn2()
+    ``kwargs`` are passed as-is to matplotlib.patches.Circle.
+    returns a list of three Circle patches.
+
+    >>> c = venn2_circles((1, 2, 3))
+    >>> c = venn2_circles({'10': 1, '01': 2, '11': 3}) # Same effect
+    >>> c = venn2_circles([set([1,2,3,4]), set([2,3,4,5,6])]) # Also same effect
+    '''
+    if isinstance(subsets, dict):
+        subsets = [subsets.get(t, 0) for t in ['10', '01', '11']]
+    elif len(subsets) == 2:
+        subsets = compute_venn2_subsets(*subsets)
+    areas = compute_venn2_areas(subsets, normalize_to)
+    centers, radii = solve_venn2_circles(areas)
+
+    if ax is None:
+        ax = gca()
+    prepare_venn_axes(ax, centers, radii)
+    result = []
+    for (c, r) in zip(centers, radii):
+        circle = Circle(c, r, alpha=alpha, edgecolor=color, facecolor='none', linestyle=linestyle, linewidth=linewidth, **kwargs)
+        ax.add_patch(circle)
+        result.append(circle)
+    return result
+
+
+def venn2(subsets, set_labels=('A', 'B'), set_colors=('r', 'g'), alpha=0.4, normalize_to=1.0, ax=None, subset_label_formatter=None):
+    '''Plots a 2-set area-weighted Venn diagram.
+    The subsets parameter can be one of the following:
+     - A list (or a tuple) containing two set objects.
+     - A dict, providing sizes of three diagram regions.
+       The regions are identified via two-letter binary codes ('10', '01', and '11'), hence a valid set could look like:
+       {'10': 10, '01': 20, '11': 40}. Unmentioned codes are considered to map to 0.
+     - A list (or a tuple) with three numbers, denoting the sizes of the regions in the following order:
+       (10, 01, 11)
+
+    ``set_labels`` parameter is a list of two strings - set labels. Set it to None to disable set labels.
+    The ``set_colors`` parameter should be a list of two elements, specifying the "base colors" of the two circles.
+    The color of circle intersection will be computed based on those.
+
+    The ``normalize_to`` parameter specifies the total (on-axes) area of the circles to be drawn. Sometimes tuning it (together
+    with the overall fiture size) may be useful to fit the text labels better.
+    The return value is a ``VennDiagram`` object, that keeps references to the ``Text`` and ``Patch`` objects used on the plot
+    and lets you know the centers and radii of the circles, if you need it.
+
+    The ``ax`` parameter specifies the axes on which the plot will be drawn (None means current axes).
+
+    The ``subset_label_formatter`` parameter is a function that can be passed to format the labels
+    that describe the size of each subset.
+
+    >>> from matplotlib_venn import *
+    >>> v = venn2(subsets={'10': 1, '01': 1, '11': 1}, set_labels = ('A', 'B'))
+    >>> c = venn2_circles(subsets=(1, 1, 1), linestyle='dashed')
+    >>> v.get_patch_by_id('10').set_alpha(1.0)
+    >>> v.get_patch_by_id('10').set_color('white')
+    >>> v.get_label_by_id('10').set_text('Unknown')
+    >>> v.get_label_by_id('A').set_text('Set A')
+
+    You can provide sets themselves rather than subset sizes:
+    >>> v = venn2(subsets=[set([1,2]), set([2,3,4,5])], set_labels = ('A', 'B'))
+    >>> c = venn2_circles(subsets=[set([1,2]), set([2,3,4,5])], linestyle='dashed')
+    >>> print("%0.2f" % (v.get_circle_radius(1)/v.get_circle_radius(0)))
+    1.41
+    '''
+    if isinstance(subsets, dict):
+        subsets = [subsets.get(t, 0) for t in ['10', '01', '11']]
+    elif len(subsets) == 2:
+        subsets = compute_venn2_subsets(*subsets)
+
+    if subset_label_formatter is None:
+        subset_label_formatter = str
+
+    areas = compute_venn2_areas(subsets, normalize_to)
+    centers, radii = solve_venn2_circles(areas)
+    regions = compute_venn2_regions(centers, radii)
+    colors = compute_venn2_colors(set_colors)
+
+    if ax is None:
+        ax = gca()
+    prepare_venn_axes(ax, centers, radii)
+
+    # Create and add patches and subset labels
+    patches = [r.make_patch() for r in regions]
+    for (p, c) in zip(patches, colors):
+        if p is not None:
+            p.set_facecolor(c)
+            p.set_edgecolor('none')
+            p.set_alpha(alpha)
+            ax.add_patch(p)
+    label_positions = [r.label_position() for r in regions]
+    subset_labels = [ax.text(lbl[0], lbl[1], subset_label_formatter(s), va='center', ha='center') if lbl is not None else None for (lbl, s) in zip(label_positions, subsets)]
+
+    # Position set labels
+    if set_labels is not None:
+        padding = np.mean([r * 0.1 for r in radii])
+        label_positions = [centers[0] + np.array([0.0, - radii[0] - padding]),
+                           centers[1] + np.array([0.0, - radii[1] - padding])]
+        labels = [ax.text(pos[0], pos[1], txt, size='large', ha='right', va='top') for (pos, txt) in zip(label_positions, set_labels)]
+        labels[1].set_ha('left')
+    else:
+        labels = None
+    return VennDiagram(patches, subset_labels, labels, centers, radii)
diff --git a/matplotlib_venn/_venn3.py b/matplotlib_venn/_venn3.py
new file mode 100644
index 0000000000000000000000000000000000000000..6b09aaac94b6cd8cb7679f4b0df4068b5d415e12
--- /dev/null
+++ b/matplotlib_venn/_venn3.py
@@ -0,0 +1,406 @@
+'''
+Venn diagram plotting routines.
+Three-circle venn plotter.
+
+Copyright 2012, Konstantin Tretyakov.
+http://kt.era.ee/
+
+Licensed under MIT license.
+'''
+import numpy as np
+import warnings
+from collections import Counter
+
+from matplotlib.patches import Circle, PathPatch
+from matplotlib.path import Path
+from matplotlib.colors import ColorConverter
+from matplotlib.pyplot import gca
+
+from matplotlib_venn._math import *
+from matplotlib_venn._common import *
+from matplotlib_venn._region import VennCircleRegion, VennEmptyRegion
+
+
+def compute_venn3_areas(diagram_areas, normalize_to=1.0, _minimal_area=1e-6):
+    '''
+    The list of venn areas is given as 7 values, corresponding to venn diagram areas in the following order:
+     (Abc, aBc, ABc, abC, AbC, aBC, ABC)
+    (i.e. last element corresponds to the size of intersection A&B&C).
+    The return value is a list of areas (A_a, A_b, A_c, A_ab, A_bc, A_ac, A_abc),
+    such that the total area of all circles is normalized to normalize_to.
+    If the area of any circle is smaller than _minimal_area, makes it equal to _minimal_area.
+
+    Assumes all input values are nonnegative (to be more precise, all areas are passed through and abs() function)
+    >>> compute_venn3_areas((1, 1, 0, 1, 0, 0, 0))
+    (0.33..., 0.33..., 0.33..., 0.0, 0.0, 0.0, 0.0)
+    >>> compute_venn3_areas((0, 0, 0, 0, 0, 0, 0))
+    (1e-06, 1e-06, 1e-06, 0.0, 0.0, 0.0, 0.0)
+    >>> compute_venn3_areas((1, 1, 1, 1, 1, 1, 1), normalize_to=7)
+    (4.0, 4.0, 4.0, 2.0, 2.0, 2.0, 1.0)
+    >>> compute_venn3_areas((1, 2, 3, 4, 5, 6, 7), normalize_to=56/2)
+    (16.0, 18.0, 22.0, 10.0, 13.0, 12.0, 7.0)
+    '''
+    # Normalize input values to sum to 1
+    areas = np.array(np.abs(diagram_areas), float)
+    total_area = np.sum(areas)
+    if np.abs(total_area) < _minimal_area:
+        warnings.warn("All circles have zero area")
+        return (1e-06, 1e-06, 1e-06, 0.0, 0.0, 0.0, 0.0)
+    else:
+        areas = areas / total_area * normalize_to
+        A_a = areas[0] + areas[2] + areas[4] + areas[6]
+        if A_a < _minimal_area:
+            warnings.warn("Circle A has zero area")
+            A_a = _minimal_area
+        A_b = areas[1] + areas[2] + areas[5] + areas[6]
+        if A_b < _minimal_area:
+            warnings.warn("Circle B has zero area")
+            A_b = _minimal_area
+        A_c = areas[3] + areas[4] + areas[5] + areas[6]
+        if A_c < _minimal_area:
+            warnings.warn("Circle C has zero area")
+            A_c = _minimal_area
+
+        # Areas of the three intersections (ab, ac, bc)
+        A_ab, A_ac, A_bc = areas[2] + areas[6], areas[4] + areas[6], areas[5] + areas[6]
+
+        return (A_a, A_b, A_c, A_ab, A_bc, A_ac, areas[6])
+
+
+def solve_venn3_circles(venn_areas):
+    '''
+    Given the list of "venn areas" (as output from compute_venn3_areas, i.e. [A, B, C, AB, BC, AC, ABC]),
+    finds the positions and radii of the three circles.
+    The return value is a tuple (coords, radii), where coords is a 3x2 array of coordinates and
+    radii is a 3x1 array of circle radii.
+
+    Assumes the input values to be nonnegative and not all zero.
+    In particular, the first three values must all be positive.
+
+    The overall match is only approximate (to be precise, what is matched are the areas of the circles and the
+    three pairwise intersections).
+
+    >>> c, r = solve_venn3_circles((1, 1, 1, 0, 0, 0, 0))
+    >>> np.round(r, 3)
+    array([ 0.564,  0.564,  0.564])
+    >>> c, r = solve_venn3_circles(compute_venn3_areas((1, 2, 40, 30, 4, 40, 4)))
+    >>> np.round(r, 3)
+    array([ 0.359,  0.476,  0.453])
+    '''
+    (A_a, A_b, A_c, A_ab, A_bc, A_ac, A_abc) = list(map(float, venn_areas))
+    r_a, r_b, r_c = np.sqrt(A_a / np.pi), np.sqrt(A_b / np.pi), np.sqrt(A_c / np.pi)
+    intersection_areas = [A_ab, A_bc, A_ac]
+    radii = np.array([r_a, r_b, r_c])
+
+    # Hypothetical distances between circle centers that assure
+    # that their pairwise intersection areas match the requirements.
+    dists = [find_distance_by_area(radii[i], radii[j], intersection_areas[i]) for (i, j) in [(0, 1), (1, 2), (2, 0)]]
+
+    # How many intersections have nonzero area?
+    num_nonzero = sum(np.array([A_ab, A_bc, A_ac]) > tol)
+
+    # Handle four separate cases:
+    #    1. All pairwise areas nonzero
+    #    2. Two pairwise areas nonzero
+    #    3. One pairwise area nonzero
+    #    4. All pairwise areas zero.
+
+    if num_nonzero == 3:
+        # The "generic" case, simply use dists to position circles at the vertices of a triangle.
+        # Before we need to ensure that resulting circles can be at all positioned on a triangle,
+        # use an ad-hoc fix.
+        for i in range(3):
+            i, j, k = (i, (i + 1) % 3, (i + 2) % 3)
+            if dists[i] > dists[j] + dists[k]:
+                a, b = (j, k) if dists[j] < dists[k] else (k, j)
+                dists[i] = dists[b] + dists[a]*0.8
+                warnings.warn("Bad circle positioning")
+        coords = position_venn3_circles_generic(radii, dists)
+    elif num_nonzero == 2:
+        # One pair of circles is not intersecting.
+        # In this case we can position all three circles in a line
+        # The two circles that have no intersection will be on either sides.
+        for i in range(3):
+            if intersection_areas[i] < tol:
+                (left, right, middle) = (i, (i + 1) % 3, (i + 2) % 3)
+                coords = np.zeros((3, 2))
+                coords[middle][0] = dists[middle]
+                coords[right][0] = dists[middle] + dists[right]
+                # We want to avoid the situation where left & right still intersect
+                if coords[left][0] + radii[left] > coords[right][0] - radii[right]:
+                    mid = (coords[left][0] + radii[left] + coords[right][0] - radii[right]) / 2.0
+                    coords[left][0] = mid - radii[left] - 1e-5
+                    coords[right][0] = mid + radii[right] + 1e-5
+                break
+    elif num_nonzero == 1:
+        # Only one pair of circles is intersecting, and one circle is independent.
+        # Position all on a line first two intersecting, then the free one.
+        for i in range(3):
+            if intersection_areas[i] > tol:
+                (left, right, side) = (i, (i + 1) % 3, (i + 2) % 3)
+                coords = np.zeros((3, 2))
+                coords[right][0] = dists[left]
+                coords[side][0] = dists[left] + radii[right] + radii[side] * 1.1  # Pad by 10%
+                break
+    else:
+        # All circles are non-touching. Put them all in a sequence
+        coords = np.zeros((3, 2))
+        coords[1][0] = radii[0] + radii[1] * 1.1
+        coords[2][0] = radii[0] + radii[1] * 1.1 + radii[1] + radii[2] * 1.1
+
+    coords = normalize_by_center_of_mass(coords, radii)
+    return (coords, radii)
+
+
+def position_venn3_circles_generic(radii, dists):
+    '''
+    Given radii = (r_a, r_b, r_c) and distances between the circles = (d_ab, d_bc, d_ac),
+    finds the coordinates of the centers for the three circles so that they form a proper triangle.
+    The current positioning method puts the center of A and B on a horizontal line y==0,
+    and C just below.
+
+    Returns a 3x2 array with circle center coordinates in rows.
+
+    >>> position_venn3_circles_generic((1, 1, 1), (0, 0, 0))
+    array([[ 0.,  0.],
+           [ 0.,  0.],
+           [ 0., -0.]])
+    >>> position_venn3_circles_generic((1, 1, 1), (2, 2, 2))
+    array([[ 0.        ,  0.        ],
+           [ 2.        ,  0.        ],
+           [ 1.        , -1.73205081]])
+    '''
+    (d_ab, d_bc, d_ac) = dists
+    (r_a, r_b, r_c) = radii
+    coords = np.array([[0, 0], [d_ab, 0], [0, 0]], float)
+    C_x = (d_ac**2 - d_bc**2 + d_ab**2) / 2.0 / d_ab if np.abs(d_ab) > tol else 0.0
+    C_y = -np.sqrt(d_ac**2 - C_x**2)
+    coords[2, :] = C_x, C_y
+    return coords
+
+
+def compute_venn3_regions(centers, radii):
+    '''
+    Given the 3x2 matrix with circle center coordinates, and a 3-element list (or array) with circle radii [as returned from solve_venn3_circles],
+    returns the 7 regions, comprising the venn diagram, as VennRegion objects.
+
+    Regions are returned in order (Abc, aBc, ABc, abC, AbC, aBC, ABC)
+
+    >>> centers, radii = solve_venn3_circles((1, 1, 1, 1, 1, 1, 1))
+    >>> regions = compute_venn3_regions(centers, radii)
+    '''
+    A = VennCircleRegion(centers[0], radii[0])
+    B = VennCircleRegion(centers[1], radii[1])
+    C = VennCircleRegion(centers[2], radii[2])
+    Ab, AB = A.subtract_and_intersect_circle(B.center, B.radius)
+    ABc, ABC = AB.subtract_and_intersect_circle(C.center, C.radius)
+    Abc, AbC = Ab.subtract_and_intersect_circle(C.center, C.radius)
+    aB, _ = B.subtract_and_intersect_circle(A.center, A.radius)
+    aBc, aBC = aB.subtract_and_intersect_circle(C.center, C.radius)
+    aC, _ = C.subtract_and_intersect_circle(A.center, A.radius)
+    abC, _ = aC.subtract_and_intersect_circle(B.center, B.radius)
+    return [Abc, aBc, ABc, abC, AbC, aBC, ABC]
+
+
+def compute_venn3_colors(set_colors):
+    '''
+    Given three base colors, computes combinations of colors corresponding to all regions of the venn diagram.
+    returns a list of 7 elements, providing colors for regions (100, 010, 110, 001, 101, 011, 111).
+
+    >>> compute_venn3_colors(['r', 'g', 'b'])
+    (array([ 1.,  0.,  0.]),..., array([ 0.4,  0.2,  0.4]))
+    '''
+    ccv = ColorConverter()
+    base_colors = [np.array(ccv.to_rgb(c)) for c in set_colors]
+    return (base_colors[0], base_colors[1], mix_colors(base_colors[0], base_colors[1]), base_colors[2],
+            mix_colors(base_colors[0], base_colors[2]), mix_colors(base_colors[1], base_colors[2]), mix_colors(base_colors[0], base_colors[1], base_colors[2]))
+
+
+def compute_venn3_subsets(a, b, c):
+    '''
+    Given three set or Counter objects, computes the sizes of (a & ~b & ~c, ~a & b & ~c, a & b & ~c, ....),
+    as needed by the subsets parameter of venn3 and venn3_circles.
+    Returns the result as a tuple.
+
+    >>> compute_venn3_subsets(set([1,2,3]), set([2,3,4]), set([3,4,5,6]))
+    (1, 0, 1, 2, 0, 1, 1)
+    >>> compute_venn3_subsets(Counter([1,2,3]), Counter([2,3,4]), Counter([3,4,5,6]))
+    (1, 0, 1, 2, 0, 1, 1)
+    >>> compute_venn3_subsets(Counter([1,1,1]), Counter([1,1,1]), Counter([1,1,1,1]))
+    (0, 0, 0, 1, 0, 0, 3)
+    >>> compute_venn3_subsets(Counter([1,1,2,2,3,3]), Counter([2,2,3,3,4,4]), Counter([3,3,4,4,5,5,6,6]))
+    (2, 0, 2, 4, 0, 2, 2)
+    >>> compute_venn3_subsets(Counter([1,2,3]), Counter([2,2,3,3,4,4]), Counter([3,3,4,4,4,5,5,6]))
+    (1, 1, 1, 4, 0, 3, 1)
+    >>> compute_venn3_subsets(set([]), set([]), set([]))
+    (0, 0, 0, 0, 0, 0, 0)
+    >>> compute_venn3_subsets(set([1]), set([]), set([]))
+    (1, 0, 0, 0, 0, 0, 0)
+    >>> compute_venn3_subsets(set([]), set([1]), set([]))
+    (0, 1, 0, 0, 0, 0, 0)
+    >>> compute_venn3_subsets(set([]), set([]), set([1]))
+    (0, 0, 0, 1, 0, 0, 0)
+    >>> compute_venn3_subsets(Counter([]), Counter([]), Counter([1]))
+    (0, 0, 0, 1, 0, 0, 0)
+    >>> compute_venn3_subsets(set([1]), set([1]), set([1]))
+    (0, 0, 0, 0, 0, 0, 1)
+    >>> compute_venn3_subsets(set([1,3,5,7]), set([2,3,6,7]), set([4,5,6,7]))
+    (1, 1, 1, 1, 1, 1, 1)
+    >>> compute_venn3_subsets(Counter([1,3,5,7]), Counter([2,3,6,7]), Counter([4,5,6,7]))
+    (1, 1, 1, 1, 1, 1, 1)
+    >>> compute_venn3_subsets(Counter([1,3,5,7]), set([2,3,6,7]), set([4,5,6,7]))
+    Traceback (most recent call last):
+    ...
+    ValueError: All arguments must be of the same type
+    '''
+    if not (type(a) == type(b) == type(c)):
+        raise ValueError("All arguments must be of the same type")
+    set_size = len if type(a) != Counter else lambda x: sum(x.values())   # We cannot use len to compute the cardinality of a Counter
+    return (set_size(a - (b | c)),  # TODO: This is certainly not the most efficient way to compute.
+        set_size(b - (a | c)),
+        set_size((a & b) - c),
+        set_size(c - (a | b)),
+        set_size((a & c) - b),
+        set_size((b & c) - a),
+        set_size(a & b & c))
+
+
+def venn3_circles(subsets, normalize_to=1.0, alpha=1.0, color='black', linestyle='solid', linewidth=2.0, ax=None, **kwargs):
+    '''
+    Plots only the three circles for the corresponding Venn diagram.
+    Useful for debugging or enhancing the basic venn diagram.
+    parameters ``subsets``, ``normalize_to`` and ``ax`` are the same as in venn3()
+    kwargs are passed as-is to matplotlib.patches.Circle.
+    returns a list of three Circle patches.
+
+        >>> plot = venn3_circles({'001': 10, '100': 20, '010': 21, '110': 13, '011': 14})
+        >>> plot = venn3_circles([set(['A','B','C']), set(['A','D','E','F']), set(['D','G','H'])])
+    '''
+    # Prepare parameters
+    if isinstance(subsets, dict):
+        subsets = [subsets.get(t, 0) for t in ['100', '010', '110', '001', '101', '011', '111']]
+    elif len(subsets) == 3:
+        subsets = compute_venn3_subsets(*subsets)
+
+    areas = compute_venn3_areas(subsets, normalize_to)
+    centers, radii = solve_venn3_circles(areas)
+
+    if ax is None:
+        ax = gca()
+    prepare_venn_axes(ax, centers, radii)
+    result = []
+    for (c, r) in zip(centers, radii):
+        circle = Circle(c, r, alpha=alpha, edgecolor=color, facecolor='none', linestyle=linestyle, linewidth=linewidth, **kwargs)
+        ax.add_patch(circle)
+        result.append(circle)
+    return result
+
+
+def venn3(subsets, set_labels=('A', 'B', 'C'), set_colors=('r', 'g', 'b'), alpha=0.4, normalize_to=1.0, ax=None, subset_label_formatter=None):
+    '''Plots a 3-set area-weighted Venn diagram.
+    The subsets parameter can be one of the following:
+     - A list (or a tuple), containing three set objects.
+     - A dict, providing sizes of seven diagram regions.
+       The regions are identified via three-letter binary codes ('100', '010', etc), hence a valid set could look like:
+       {'001': 10, '010': 20, '110':30, ...}. Unmentioned codes are considered to map to 0.
+     - A list (or a tuple) with 7 numbers, denoting the sizes of the regions in the following order:
+       (100, 010, 110, 001, 101, 011, 111).
+
+    ``set_labels`` parameter is a list of three strings - set labels. Set it to None to disable set labels.
+    The ``set_colors`` parameter should be a list of three elements, specifying the "base colors" of the three circles.
+    The colors of circle intersections will be computed based on those.
+
+    The ``normalize_to`` parameter specifies the total (on-axes) area of the circles to be drawn. Sometimes tuning it (together
+    with the overall fiture size) may be useful to fit the text labels better.
+    The return value is a ``VennDiagram`` object, that keeps references to the ``Text`` and ``Patch`` objects used on the plot
+    and lets you know the centers and radii of the circles, if you need it.
+
+    The ``ax`` parameter specifies the axes on which the plot will be drawn (None means current axes).
+
+    The ``subset_label_formatter`` parameter is a function that can be passed to format the labels
+    that describe the size of each subset.
+
+    Note: if some of the circles happen to have zero area, you will probably not get a nice picture.
+
+    >>> import matplotlib # (The first two lines prevent the doctest from falling when TCL not installed. Not really necessary in most cases)
+    >>> matplotlib.use('Agg')
+    >>> from matplotlib_venn import *
+    >>> v = venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C'))
+    >>> c = venn3_circles(subsets=(1, 1, 1, 1, 1, 1, 1), linestyle='dashed')
+    >>> v.get_patch_by_id('100').set_alpha(1.0)
+    >>> v.get_patch_by_id('100').set_color('white')
+    >>> v.get_label_by_id('100').set_text('Unknown')
+    >>> v.get_label_by_id('C').set_text('Set C')
+
+    You can provide sets themselves rather than subset sizes:
+    >>> v = venn3(subsets=[set([1,2]), set([2,3,4,5]), set([4,5,6,7,8,9,10,11])])
+    >>> print("%0.2f %0.2f %0.2f" % (v.get_circle_radius(0), v.get_circle_radius(1)/v.get_circle_radius(0), v.get_circle_radius(2)/v.get_circle_radius(0)))
+    0.24 1.41 2.00
+    >>> c = venn3_circles(subsets=[set([1,2]), set([2,3,4,5]), set([4,5,6,7,8,9,10,11])])
+    '''
+    # Prepare parameters
+    if isinstance(subsets, dict):
+        subsets = [subsets.get(t, 0) for t in ['100', '010', '110', '001', '101', '011', '111']]
+    elif len(subsets) == 3:
+        subsets = compute_venn3_subsets(*subsets)
+
+    if subset_label_formatter is None:
+        subset_label_formatter = str
+
+    areas = compute_venn3_areas(subsets, normalize_to)
+    centers, radii = solve_venn3_circles(areas)
+    regions = compute_venn3_regions(centers, radii)
+    colors = compute_venn3_colors(set_colors)
+
+    # Remove regions that are too small from the diagram
+    MIN_REGION_SIZE = 1e-4
+    for i in range(len(regions)):
+        if regions[i].size() < MIN_REGION_SIZE and subsets[i] == 0:
+            regions[i] = VennEmptyRegion()
+
+    # There is a rare case (Issue #12) when the middle region is visually empty
+    # (the positioning of the circles does not let them intersect), yet the corresponding value is not 0.
+    # we address it separately here by positioning the label of that empty region in a custom way
+    if isinstance(regions[6], VennEmptyRegion) and subsets[6] > 0:
+        intersections = [circle_circle_intersection(centers[i], radii[i], centers[j], radii[j]) for (i, j) in [(0, 1), (1, 2), (2, 0)]]
+        middle_pos = np.mean([i[0] for i in intersections], 0)
+        regions[6] = VennEmptyRegion(middle_pos)
+
+    if ax is None:
+        ax = gca()
+    prepare_venn_axes(ax, centers, radii)
+
+    # Create and add patches and text
+    patches = [r.make_patch() for r in regions]
+    for (p, c) in zip(patches, colors):
+        if p is not None:
+            p.set_facecolor(c)
+            p.set_edgecolor('none')
+            p.set_alpha(alpha)
+            ax.add_patch(p)
+    label_positions = [r.label_position() for r in regions]
+    subset_labels = [ax.text(lbl[0], lbl[1], subset_label_formatter(s), va='center', ha='center') if lbl is not None else None for (lbl, s) in zip(label_positions, subsets)]
+
+    # Position labels
+    if set_labels is not None:
+        # There are two situations, when set C is not on the same line with sets A and B, and when the three are on the same line.
+        if abs(centers[2][1] - centers[0][1]) > tol:
+            # Three circles NOT on the same line
+            label_positions = [centers[0] + np.array([-radii[0] / 2, radii[0]]),
+                               centers[1] + np.array([radii[1] / 2, radii[1]]),
+                               centers[2] + np.array([0.0, -radii[2] * 1.1])]
+            labels = [ax.text(pos[0], pos[1], txt, size='large') for (pos, txt) in zip(label_positions, set_labels)]
+            labels[0].set_horizontalalignment('right')
+            labels[1].set_horizontalalignment('left')
+            labels[2].set_verticalalignment('top')
+            labels[2].set_horizontalalignment('center')
+        else:
+            padding = np.mean([r * 0.1 for r in radii])
+            # Three circles on the same line
+            label_positions = [centers[0] + np.array([0.0, - radii[0] - padding]),
+                               centers[1] + np.array([0.0, - radii[1] - padding]),
+                               centers[2] + np.array([0.0, - radii[2] - padding])]
+            labels = [ax.text(pos[0], pos[1], txt, size='large', ha='center', va='top') for (pos, txt) in zip(label_positions, set_labels)]
+    else:
+        labels = None
+    return VennDiagram(patches, subset_labels, labels, centers, radii)
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e9c91125ce5185fba291264316f1121119e56136
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,9 @@
+[egg_info]
+tag_build = 
+tag_svn_revision = 0
+tag_date = 0
+
+[pytest]
+addopts = --ignore=setup.py --ignore=build --ignore=dist --doctest-modules
+norecursedirs = *.egg
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..1825cfc9c6a2276623378709558ec5703a0968c7
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,52 @@
+'''
+Venn diagram plotting routines.
+Setup script.
+
+Note that "python setup.py test" invokes pytest on the package. This checks both xxx_test modules and docstrings.
+
+Copyright 2012, Konstantin Tretyakov.
+http://kt.era.ee/
+
+Licensed under MIT license.
+'''
+
+from setuptools import setup, find_packages
+
+from setuptools.command.test import test as TestCommand
+
+
+class PyTest(TestCommand):
+    def run_tests(self):
+        import sys
+        import pytest  # import here, cause outside the eggs aren't loaded
+        sys.exit(pytest.main(self.test_args))
+
+version = '0.11.5'
+
+setup(name='matplotlib-venn',
+      version=version,
+      description="Functions for plotting area-proportional two- and three-way Venn diagrams in matplotlib.",
+      long_description=open("README.rst").read(),
+      classifiers=[  # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+          'Development Status :: 4 - Beta',
+          'Intended Audience :: Science/Research',
+          'License :: OSI Approved :: MIT License',
+          'Operating System :: OS Independent',
+          'Programming Language :: Python :: 2',
+          'Programming Language :: Python :: 3',
+          'Topic :: Scientific/Engineering :: Visualization'
+      ],
+      platforms=['Platform Independent'],
+      keywords='matplotlib plotting charts venn-diagrams',
+      author='Konstantin Tretyakov',
+      author_email='kt@ut.ee',
+      url='https://github.com/konstantint/matplotlib-venn',
+      license='MIT',
+      packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+      include_package_data=True,
+      zip_safe=True,
+      install_requires=['matplotlib', 'numpy', 'scipy'],
+      tests_require=['pytest'],
+      cmdclass={'test': PyTest},
+      entry_points=''
+      )