Skip to content

Commit d1c7743

Browse files
authored
Merge pull request matplotlib#19623 from ianthomas23/19568_closed_contour_lines
FIX: Contour lines rendered incorrectly when closed loops
2 parents c7a01db + a97ab90 commit d1c7743

File tree

24 files changed

+2381
-1250
lines changed

24 files changed

+2381
-1250
lines changed

lib/matplotlib/contour.py

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -823,16 +823,18 @@ def __init__(self, ax, *args,
823823
self.norm.vmax = vmax
824824
self._process_colors()
825825

826-
self.allsegs, self.allkinds = self._get_allsegs_and_allkinds()
826+
if getattr(self, 'allsegs', None) is None:
827+
self.allsegs, self.allkinds = self._get_allsegs_and_allkinds()
828+
elif self.allkinds is None:
829+
# allsegs specified in constructor may or may not have allkinds as
830+
# well. Must ensure allkinds can be zipped below.
831+
self.allkinds = [None] * len(self.allsegs)
827832

828833
if self.filled:
829834
if self.linewidths is not None:
830835
_api.warn_external('linewidths is ignored by contourf')
831836
# Lower and upper contour levels.
832837
lowers, uppers = self._get_lowers_and_uppers()
833-
# Ensure allkinds can be zipped below.
834-
if self.allkinds is None:
835-
self.allkinds = [None] * len(self.allsegs)
836838
# Default zorder taken from Collection
837839
self._contour_zorder = kwargs.pop('zorder', 1)
838840

@@ -852,21 +854,24 @@ def __init__(self, ax, *args,
852854
aa = self.antialiased
853855
if aa is not None:
854856
aa = (self.antialiased,)
855-
# Default zorder taken from LineCollection
857+
# Default zorder taken from LineCollection, which is higher than
858+
# for filled contours so that lines are displayed on top.
856859
self._contour_zorder = kwargs.pop('zorder', 2)
857860

858861
self.collections[:] = [
859-
mcoll.LineCollection(
860-
segs,
862+
mcoll.PathCollection(
863+
self._make_paths(segs, kinds),
864+
facecolors="none",
861865
antialiaseds=aa,
862866
linewidths=width,
863867
linestyles=[lstyle],
864868
alpha=self.alpha,
865869
transform=self.get_transform(),
866870
zorder=self._contour_zorder,
867871
label='_nolegend_')
868-
for level, width, lstyle, segs
869-
in zip(self.levels, tlinewidths, tlinestyles, self.allsegs)]
872+
for level, width, lstyle, segs, kinds
873+
in zip(self.levels, tlinewidths, tlinestyles, self.allsegs,
874+
self.allkinds)]
870875

871876
for col in self.collections:
872877
self.axes.add_collection(col, autolim=False)
@@ -998,11 +1003,23 @@ def _process_args(self, *args, **kwargs):
9981003
return kwargs
9991004

10001005
def _get_allsegs_and_allkinds(self):
1001-
"""
1002-
Override in derived classes to create and return allsegs and allkinds.
1003-
allkinds can be None.
1004-
"""
1005-
return self.allsegs, self.allkinds
1006+
"""Compute ``allsegs`` and ``allkinds`` using C extension."""
1007+
allsegs = []
1008+
allkinds = []
1009+
if self.filled:
1010+
lowers, uppers = self._get_lowers_and_uppers()
1011+
for level, level_upper in zip(lowers, uppers):
1012+
vertices, kinds = \
1013+
self._contour_generator.create_filled_contour(
1014+
level, level_upper)
1015+
allsegs.append(vertices)
1016+
allkinds.append(kinds)
1017+
else:
1018+
for level in self.levels:
1019+
vertices, kinds = self._contour_generator.create_contour(level)
1020+
allsegs.append(vertices)
1021+
allkinds.append(kinds)
1022+
return allsegs, allkinds
10061023

10071024
def _get_lowers_and_uppers(self):
10081025
"""
@@ -1020,11 +1037,21 @@ def _get_lowers_and_uppers(self):
10201037
return (lowers, uppers)
10211038

10221039
def _make_paths(self, segs, kinds):
1023-
if kinds is not None:
1024-
return [mpath.Path(seg, codes=kind)
1025-
for seg, kind in zip(segs, kinds)]
1026-
else:
1040+
"""
1041+
Create and return Path objects for the specified segments and optional
1042+
kind codes. segs is a list of numpy arrays, each array is either a
1043+
closed line loop or open line strip of 2D points with a shape of
1044+
(npoints, 2). kinds is either None or a list (with the same length as
1045+
segs) of numpy arrays, each array is of shape (npoints,) and contains
1046+
the kinds codes for the corresponding line in segs. If kinds is None
1047+
then the Path constructor creates the kind codes assuming that the line
1048+
is an open strip.
1049+
"""
1050+
if kinds is None:
10271051
return [mpath.Path(seg) for seg in segs]
1052+
else:
1053+
return [mpath.Path(seg, codes=kind) for seg, kind
1054+
in zip(segs, kinds)]
10281055

10291056
def changed(self):
10301057
tcolors = [(tuple(rgba),)
@@ -1038,7 +1065,7 @@ def changed(self):
10381065
# update the collection's hatch (may be None)
10391066
collection.set_hatch(hatch)
10401067
else:
1041-
collection.set_color(color)
1068+
collection.set_edgecolor(color)
10421069
for label, cv in zip(self.labelTexts, self.labelCValues):
10431070
label.set_alpha(self.alpha)
10441071
label.set_color(self.labelMappable.to_rgba(cv))
@@ -1391,25 +1418,6 @@ def _process_args(self, *args, corner_mask=None, **kwargs):
13911418

13921419
return kwargs
13931420

1394-
def _get_allsegs_and_allkinds(self):
1395-
"""Compute ``allsegs`` and ``allkinds`` using C extension."""
1396-
allsegs = []
1397-
if self.filled:
1398-
lowers, uppers = self._get_lowers_and_uppers()
1399-
allkinds = []
1400-
for level, level_upper in zip(lowers, uppers):
1401-
vertices, kinds = \
1402-
self._contour_generator.create_filled_contour(
1403-
level, level_upper)
1404-
allsegs.append(vertices)
1405-
allkinds.append(kinds)
1406-
else:
1407-
allkinds = None
1408-
for level in self.levels:
1409-
vertices = self._contour_generator.create_contour(level)
1410-
allsegs.append(vertices)
1411-
return allsegs, allkinds
1412-
14131421
def _contour_args(self, args, kwargs):
14141422
if self.filled:
14151423
fn = 'contourf'
Binary file not shown.
-13 Bytes
Loading

0 commit comments

Comments
 (0)