Skip to content

Commit 556b4d1

Browse files
committed
New C++ contour code with corner_mask
1 parent ca10a34 commit 556b4d1

21 files changed

+10404
-7895
lines changed

CHANGELOG

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
2014-12-02 Rewrite of C++ code that calculates contours to add support for
2+
corner masking. This is controlled by the 'corner_mask' keyword
3+
in plotting commands 'contour' and 'contourf'. - IMT
4+
15
2014-10-27 Allowed selection of the backend using the `MPLBACKEND` environment
26
variable. Added documentation on backend selection methods.
37

48
2014-09-27 Overhauled `colors.LightSource`. Added `LightSource.hillshade` to
5-
allow the independent generation of illumination maps. Added new
6-
types of blending for creating more visually appealing shaded relief
7-
plots (e.g. `blend_mode="overlay"`, etc, in addition to the legacy
8-
"hsv" mode).
9+
allow the independent generation of illumination maps. Added new
10+
types of blending for creating more visually appealing shaded relief
11+
plots (e.g. `blend_mode="overlay"`, etc, in addition to the legacy
12+
"hsv" mode).
913

1014
2014-06-10 Added Colorbar.remove()
1115

@@ -61,7 +65,7 @@
6165
of a generator at line 263.
6266

6367
2014-04-02 Added `clipon=False` to patch creation of wedges and shadows
64-
in `pie`.
68+
in `pie`.
6569

6670
2014-02-25 In backend_qt4agg changed from using update -> repaint under
6771
windows. See comment in source near `self._priv_update` for
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Changed behaviour of contour plots
2+
``````````````````````````````````
3+
4+
The default behaviour of :func:`~matplotlib.pyplot.contour` and
5+
:func:`~matplotlib.pyplot.contourf` when using a masked array is now determined
6+
by the new keyword argument `corner_mask`, or if this is not specified then
7+
the new rcParam `contour.corner_mask` instead. The new default behaviour is
8+
equivalent to using `corner_mask=True`; the previous behaviour can be obtained
9+
using `corner_mask=False` or by changing the rcParam. The example
10+
`contour_corner_mask.py` demonstrates the difference.
11+
12+
In addition, the keyword argument `nchunk` now applies to
13+
:func:`~matplotlib.pyplot.contour` as well as
14+
:func:`~matplotlib.pyplot.contourf`, and it subdivides the domain into
15+
subdomains of exactly `nchunk` by `nchunk` quads, whereas previously it was
16+
only roughly `nchunk` by `nchunk` quads.

doc/users/whats_new/plotting.rst

+16
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,19 @@ Added center and frame kwargs to pie
33

44
These control where the center of the pie graph are and if
55
the Axes frame is shown.
6+
7+
Contour plot corner masking
8+
```````````````````````````
9+
10+
Ian Thomas rewrote the C++ code that calculates contours to add support for
11+
corner masking. This is controlled by a new keyword argument
12+
``corner_mask`` in the functions :func:`~matplotlib.pyplot.contour` and
13+
:func:`~matplotlib.pyplot.contourf`. The previous behaviour, which is now
14+
obtained using ``corner_mask=False``, was for a single masked point to
15+
completely mask out all four quads touching that point. The new behaviour,
16+
obtained using ``corner_mask=True``, only masks the corners of those
17+
quads touching the point; any triangular corners comprising three unmasked
18+
points are contoured as usual. If the ``corner_mask`` keyword argument is not
19+
specified, the default value is taken from rcParams.
20+
21+
.. plot:: mpl_examples/pylab_examples/contour_corner_mask.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env python
2+
"""
3+
Illustrate the difference between corner_mask=False and corner_mask=True
4+
for masked contour plots.
5+
"""
6+
import matplotlib.pyplot as plt
7+
import numpy as np
8+
9+
# Data to plot.
10+
x, y = np.meshgrid(np.arange(7), np.arange(10))
11+
z = np.sin(0.5*x)*np.cos(0.52*y)
12+
13+
# Mask various z values.
14+
mask = np.zeros_like(z, dtype=np.bool)
15+
mask[2, 3:5] = True
16+
mask[3:5, 4] = True
17+
mask[7, 2] = True
18+
mask[5, 0] = True
19+
mask[0, 6] = True
20+
z = np.ma.array(z, mask=mask)
21+
22+
corner_masks = [False, True]
23+
for i, corner_mask in enumerate(corner_masks):
24+
plt.subplot(1, 2, i+1)
25+
cs = plt.contourf(x, y, z, corner_mask=corner_mask)
26+
plt.contour(cs, colors='k')
27+
plt.title('corner_mask = {}'.format(corner_mask))
28+
29+
# Plot grid.
30+
plt.grid(c='k', ls='-', alpha=0.3)
31+
32+
# Indicate masked points with red circles.
33+
plt.plot(np.ma.array(x, mask=~mask), y, 'ro')
34+
35+
plt.show()

lib/matplotlib/contour.py

+54-17
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import numpy as np
1414
from numpy import ma
1515
import matplotlib._cntr as _cntr
16+
import matplotlib._contour as _contour
1617
import matplotlib.path as mpath
1718
import matplotlib.ticker as ticker
1819
import matplotlib.cm as cm
@@ -1438,13 +1439,23 @@ def _process_args(self, *args, **kwargs):
14381439
self.levels = args[0].levels
14391440
self.zmin = args[0].zmin
14401441
self.zmax = args[0].zmax
1442+
self._corner_mask = args[0]._corner_mask
14411443
else:
14421444
x, y, z = self._contour_args(args, kwargs)
14431445

14441446
_mask = ma.getmask(z)
1445-
if _mask is ma.nomask:
1447+
if _mask is ma.nomask or not _mask.any():
14461448
_mask = None
1447-
C = _cntr.Cntr(x, y, z.filled(), _mask)
1449+
1450+
self._corner_mask = kwargs.get('corner_mask', None)
1451+
if self._corner_mask is None:
1452+
self._corner_mask = mpl.rcParams['contour.corner_mask']
1453+
1454+
if self._corner_mask == 'legacy':
1455+
C = _cntr.Cntr(x, y, z.filled(), _mask)
1456+
else:
1457+
C = _contour.QuadContourGenerator(
1458+
x, y, z.filled(), _mask, self._corner_mask, self.nchunk)
14481459

14491460
t = self.get_transform()
14501461

@@ -1476,19 +1487,27 @@ def _get_allsegs_and_allkinds(self):
14761487
lowers, uppers = self._get_lowers_and_uppers()
14771488
allkinds = []
14781489
for level, level_upper in zip(lowers, uppers):
1479-
nlist = self.Cntr.trace(level, level_upper,
1480-
nchunk=self.nchunk)
1481-
nseg = len(nlist) // 2
1482-
segs = nlist[:nseg]
1483-
kinds = nlist[nseg:]
1490+
if self._corner_mask == 'legacy':
1491+
nlist = self.Cntr.trace(level, level_upper,
1492+
nchunk=self.nchunk)
1493+
nseg = len(nlist) // 2
1494+
segs = nlist[:nseg]
1495+
kinds = nlist[nseg:]
1496+
else:
1497+
nlist = self.Cntr.create_filled_contour(level, level_upper)
1498+
segs = nlist[::2]
1499+
kinds = nlist[1::2]
14841500
allsegs.append(segs)
14851501
allkinds.append(kinds)
14861502
else:
14871503
allkinds = None
14881504
for level in self.levels:
1489-
nlist = self.Cntr.trace(level)
1490-
nseg = len(nlist) // 2
1491-
segs = nlist[:nseg]
1505+
if self._corner_mask == 'legacy':
1506+
nlist = self.Cntr.trace(level)
1507+
nseg = len(nlist) // 2
1508+
segs = nlist[:nseg]
1509+
else:
1510+
segs = self.Cntr.create_contour(level)
14921511
allsegs.append(segs)
14931512
return allsegs, allkinds
14941513

@@ -1672,6 +1691,20 @@ def _initialize_x_y(self, z):
16721691
16731692
Optional keyword arguments:
16741693
1694+
*corner_mask*: [ *True* | *False* | 'legacy' ]
1695+
Enable/disable corner masking, which only has an effect if *Z* is
1696+
a masked array. If *False*, any quad touching a masked point is
1697+
masked out. If *True*, only the triangular corners of quads
1698+
nearest those points are always masked out, other triangular
1699+
corners comprising three unmasked points are contoured as usual.
1700+
If 'legacy', the old contouring algorithm is used, which is
1701+
equivalent to *False* and is deprecated, only remaining whilst the
1702+
new algorithm is tested fully.
1703+
1704+
If not specified, the default is taken from
1705+
rcParams['contour.corner_mask'], which is True unless it has
1706+
been modified.
1707+
16751708
*colors*: [ *None* | string | (mpl_colors) ]
16761709
If *None*, the colormap specified by cmap will be used.
16771710
@@ -1750,6 +1783,15 @@ def _initialize_x_y(self, z):
17501783
filled contours, the default is *True*. For line contours,
17511784
it is taken from rcParams['lines.antialiased'].
17521785
1786+
*nchunk*: [ 0 | integer ]
1787+
If 0, no subdivision of the domain. Specify a positive integer to
1788+
divide the domain into subdomains of *nchunk* by *nchunk* quads.
1789+
Chunking reduces the maximum length of polygons generated by the
1790+
contouring algorithm which reduces the rendering workload passed
1791+
on to the backend and also requires slightly less RAM. It can
1792+
however introduce rendering artifacts at chunk boundaries depending
1793+
on the backend, the *antialiased* flag and value of *alpha*.
1794+
17531795
contour-only keyword arguments:
17541796
17551797
*linewidths*: [ *None* | number | tuple of numbers ]
@@ -1774,13 +1816,6 @@ def _initialize_x_y(self, z):
17741816
17751817
contourf-only keyword arguments:
17761818
1777-
*nchunk*: [ 0 | integer ]
1778-
If 0, no subdivision of the domain. Specify a positive integer to
1779-
divide the domain into subdomains of roughly *nchunk* by *nchunk*
1780-
points. This may never actually be advantageous, so this option may
1781-
be removed. Chunking introduces artifacts at the chunk boundaries
1782-
unless *antialiased* is *False*.
1783-
17841819
*hatches*:
17851820
A list of cross hatch patterns to use on the filled areas.
17861821
If None, no hatching will be added to the contour.
@@ -1802,4 +1837,6 @@ def _initialize_x_y(self, z):
18021837
.. plot:: mpl_examples/pylab_examples/contour_demo.py
18031838
18041839
.. plot:: mpl_examples/pylab_examples/contourf_demo.py
1840+
1841+
.. plot:: mpl_examples/pylab_examples/contour_corner_mask.py
18051842
"""

lib/matplotlib/rcsetup.py

+9
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,13 @@ def validate_negative_linestyle_legacy(s):
379379
return (0, dashes) # (offset, (solid, blank))
380380

381381

382+
def validate_corner_mask(s):
383+
if s == 'legacy':
384+
return s
385+
else:
386+
return validate_bool(s)
387+
388+
382389
def validate_tkpythoninspect(s):
383390
# Introduced 2010/07/05
384391
warnings.warn("tk.pythoninspect is obsolete, and has no effect")
@@ -578,8 +585,10 @@ def __call__(self, s):
578585
'image.origin': ['upper', six.text_type], # lookup table
579586
'image.resample': [False, validate_bool],
580587

588+
# contour props
581589
'contour.negative_linestyle': ['dashed',
582590
validate_negative_linestyle_legacy],
591+
'contour.corner_mask': [True, validate_corner_mask],
583592

584593
# axes props
585594
'axes.axisbelow': [False, validate_bool],
Binary file not shown.

0 commit comments

Comments
 (0)