Skip to content

Commit f703b1b

Browse files
committed
Further improvements to contouring following review
1 parent f7c9d34 commit f703b1b

File tree

3 files changed

+74
-16
lines changed

3 files changed

+74
-16
lines changed

lib/matplotlib/contour.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,12 +1435,15 @@ def _process_args(self, *args, **kwargs):
14351435
Process args and kwargs.
14361436
"""
14371437
if isinstance(args[0], QuadContourSet):
1438-
contour_generator = args[0]._contour_generator
14391438
if self.levels is None:
14401439
self.levels = args[0].levels
14411440
self.zmin = args[0].zmin
14421441
self.zmax = args[0].zmax
14431442
self._corner_mask = args[0]._corner_mask
1443+
if self._corner_mask == 'legacy':
1444+
contour_generator = args[0].Cntr
1445+
else:
1446+
contour_generator = args[0]._contour_generator
14441447
else:
14451448
x, y, z = self._contour_args(args, kwargs)
14461449

@@ -1480,7 +1483,10 @@ def _process_args(self, *args, **kwargs):
14801483
self.ax.update_datalim([(x0, y0), (x1, y1)])
14811484
self.ax.autoscale_view(tight=True)
14821485

1483-
self._contour_generator = contour_generator
1486+
if self._corner_mask == 'legacy':
1487+
self.Cntr = contour_generator
1488+
else:
1489+
self._contour_generator = contour_generator
14841490

14851491
def _get_allsegs_and_allkinds(self):
14861492
"""
@@ -1492,8 +1498,8 @@ def _get_allsegs_and_allkinds(self):
14921498
allkinds = []
14931499
for level, level_upper in zip(lowers, uppers):
14941500
if self._corner_mask == 'legacy':
1495-
nlist = self._contour_generator.trace(level, level_upper,
1496-
nchunk=self.nchunk)
1501+
nlist = self.Cntr.trace(level, level_upper,
1502+
nchunk=self.nchunk)
14971503
nseg = len(nlist) // 2
14981504
vertices = nlist[:nseg]
14991505
kinds = nlist[nseg:]
@@ -1507,7 +1513,7 @@ def _get_allsegs_and_allkinds(self):
15071513
allkinds = None
15081514
for level in self.levels:
15091515
if self._corner_mask == 'legacy':
1510-
nlist = self._contour_generator.trace(level)
1516+
nlist = self.Cntr.trace(level)
15111517
nseg = len(nlist) // 2
15121518
vertices = nlist[:nseg]
15131519
else:

src/_contour.cpp

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -566,9 +566,7 @@ PyObject* QuadContourGenerator::create_contour(const double& level)
566566
if (EXISTS_NONE(quad) || VISITED(quad,1))
567567
continue;
568568

569-
Edge start_edge =
570-
(EXISTS_ANY_CORNER(quad) ? get_corner_start_edge(quad, 1)
571-
: get_quad_start_edge(quad, 1));
569+
Edge start_edge = get_start_edge(quad, 1);
572570
if (start_edge == Edge_None)
573571
continue;
574572

@@ -701,7 +699,7 @@ unsigned int QuadContourGenerator::follow_boundary(
701699

702700
if (level_index == 1) {
703701
if (start_level <= level_index && end_level == 2) {
704-
// Increasing z, switching levels.
702+
// Increasing z, switching levels from 1 to 2.
705703
level_index = 2;
706704
stop = true;
707705
}
@@ -716,7 +714,7 @@ unsigned int QuadContourGenerator::follow_boundary(
716714
stop = true;
717715
}
718716
else if (start_level >= 1 && end_level == 0) {
719-
// Decreasing z, switching levels.
717+
// Decreasing z, switching levels from 2 to 1.
720718
level_index = 1;
721719
stop = true;
722720
}
@@ -834,13 +832,19 @@ void QuadContourGenerator::follow_interior(ContourLine& contour_line,
834832
assert(!EXISTS_NONE(quad) && "Quad does not exist");
835833
assert(!(_cache[quad] & visited_mask) && "Quad already visited");
836834

837-
// Determine direction to move to next quad.
835+
// Determine direction to move to next quad. If the quad is already
836+
// labelled as a saddle quad then the direction is easily read from
837+
// the cache. Otherwise the direction is determined differently
838+
// depending on whether the quad is a corner quad or not.
839+
838840
if (_cache[quad] & saddle_mask) {
839841
// Already identified as a saddle quad, so direction is easy.
840842
dir = (SADDLE_LEFT(quad,level_index) ? Dir_Left : Dir_Right);
841843
_cache[quad] |= visited_mask;
842844
}
843845
else if (EXISTS_ANY_CORNER(quad)) {
846+
// Need z-level of point opposite the entry edge, as that
847+
// determines whether contour turns left or right.
844848
long point_opposite = -1;
845849
switch (edge) {
846850
case Edge_E:
@@ -867,6 +871,10 @@ void QuadContourGenerator::follow_interior(ContourLine& contour_line,
867871
}
868872
assert(point_opposite != -1 && "Failed to find opposite point");
869873

874+
// Lower-level polygons (level_index == 1) always have higher
875+
// values to the left of the contour. Upper-level contours
876+
// (level_index == 2) are reversed, which is what the fancy XOR
877+
// does below.
870878
if ((Z_LEVEL(point_opposite) >= level_index) ^ (level_index == 2))
871879
dir = Dir_Right;
872880
else
@@ -891,7 +899,7 @@ void QuadContourGenerator::follow_interior(ContourLine& contour_line,
891899
// lower level ones, i.e. higher values on the right rather than
892900
// the left.
893901
if (level_index == 2)
894-
config = 3-config;
902+
config = 3 - config;
895903

896904
// Calculate turn direction to move to next quad along contour line.
897905
if (config == 1) {
@@ -1018,7 +1026,7 @@ Edge QuadContourGenerator::get_corner_start_edge(long quad,
10181026
// Upper level (level_index == 2) polygons are reversed compared to lower
10191027
// level ones, i.e. higher values on the right rather than the left.
10201028
if (level_index == 2)
1021-
config = 7-config;
1029+
config = 7 - config;
10221030

10231031
switch (config) {
10241032
case 0: return Edge_None;
@@ -1040,6 +1048,20 @@ long QuadContourGenerator::get_edge_point_index(const QuadEdge& quad_edge,
10401048
"Quad index out of bounds");
10411049
assert(quad_edge.edge != Edge_None && "Invalid edge");
10421050

1051+
// Edges are ordered anticlockwise around their quad, as indicated by
1052+
// directions of arrows in diagrams below.
1053+
// Full quad NW corner (others similar)
1054+
//
1055+
// POINT_NW Edge_N POINT_NE POINT_NW Edge_N POINT_NE
1056+
// +----<-----+ +----<-----+
1057+
// | | | /
1058+
// | | | quad /
1059+
// Edge_W V quad ^ Edge_E Edge_W V ^
1060+
// | | | / Edge_SE
1061+
// | | | /
1062+
// +---->-----+ +
1063+
// POINT_SW Edge_S POINT_SE POINT_SW
1064+
//
10431065
const long& quad = quad_edge.quad;
10441066
switch (quad_edge.edge) {
10451067
case Edge_E: return (start ? POINT_SE : POINT_NE);
@@ -1064,6 +1086,9 @@ Edge QuadContourGenerator::get_exit_edge(const QuadEdge& quad_edge,
10641086
const long& quad = quad_edge.quad;
10651087
const Edge& edge = quad_edge.edge;
10661088
if (EXISTS_ANY_CORNER(quad)) {
1089+
// Corner directions are always left or right. A corner is a triangle,
1090+
// entered via one edge so the other two edges are the left and right
1091+
// ones.
10671092
switch (edge) {
10681093
case Edge_E:
10691094
return (EXISTS_SE_CORNER(quad)
@@ -1089,6 +1114,8 @@ Edge QuadContourGenerator::get_exit_edge(const QuadEdge& quad_edge,
10891114
}
10901115
}
10911116
else {
1117+
// A full quad has four edges, entered via one edge so that other three
1118+
// edges correspond to left, straight and right directions.
10921119
switch (edge) {
10931120
case Edge_E:
10941121
return (dir == Dir_Left ? Edge_S :
@@ -1172,6 +1199,15 @@ Edge QuadContourGenerator::get_quad_start_edge(long quad,
11721199
}
11731200
}
11741201

1202+
Edge QuadContourGenerator::get_start_edge(long quad,
1203+
unsigned int level_index) const
1204+
{
1205+
if (EXISTS_ANY_CORNER(quad))
1206+
return get_corner_start_edge(quad, level_index);
1207+
else
1208+
return get_quad_start_edge(quad, level_index);
1209+
}
1210+
11751211
void QuadContourGenerator::init_cache_grid(const MaskArray& mask)
11761212
{
11771213
long i, j, quad;
@@ -1230,9 +1266,11 @@ void QuadContourGenerator::init_cache_grid(const MaskArray& mask)
12301266
}
12311267
}
12321268

1233-
// Stage 2, calculate W and S boundaries. For each quad use data
1234-
// already calculated for quads to W and S, so must iterate through
1235-
// quads in correct order (increasing i and j indices).
1269+
// Stage 2, calculate W and S boundaries. For each quad use boundary
1270+
// data already calculated for quads to W and S, so must iterate
1271+
// through quads in correct order (increasing i and j indices).
1272+
// Cannot use boundary data for quads to E and N as have not yet
1273+
// calculated it.
12361274
quad = 0;
12371275
for (j = 0; j < _ny; ++j) {
12381276
for (i = 0; i < _nx; ++i, ++quad) {
@@ -1445,6 +1483,8 @@ void QuadContourGenerator::move_to_next_quad(QuadEdge& quad_edge) const
14451483
"Quad index out of bounds");
14461484
assert(quad_edge.edge != Edge_None && "Invalid edge");
14471485

1486+
// Move from quad_edge.quad to the neighbouring quad in the direction
1487+
// specified by quad_edge.edge.
14481488
switch (quad_edge.edge) {
14491489
case Edge_E: quad_edge.quad += 1; quad_edge.edge = Edge_W; break;
14501490
case Edge_N: quad_edge.quad += _nx; quad_edge.edge = Edge_S; break;
@@ -1626,6 +1666,13 @@ void QuadContourGenerator::single_quad_filled(Contour& contour,
16261666
(!SADDLE(quad,1) || !SADDLE_LEFT(quad,1)))
16271667
contour.push_back(start_filled(quad, Edge_E, 1, Hole, Interior,
16281668
lower_level, upper_level));
1669+
1670+
// All possible contours passing through the interior of this quad
1671+
// should have already been created, so assert this.
1672+
assert((VISITED(quad,1) || get_start_edge(quad, 1) == Edge_None) &&
1673+
"Found start of contour that should have already been created");
1674+
assert((VISITED(quad,2) || get_start_edge(quad, 2) == Edge_None) &&
1675+
"Found start of contour that should have already been created");
16291676
}
16301677

16311678
// Lower-level start following N boundary from E to W, hole.

src/_contour.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ class QuadContourGenerator
428428
// return Edge_None.
429429
Edge get_quad_start_edge(long quad, unsigned int level_index) const;
430430

431+
// Check if a contour starts within the specified quad, whether it is a
432+
// corner or a full quad, and if so return the start edge. Otherwise
433+
// return Edge_None.
434+
Edge get_start_edge(long quad, unsigned int level_index) const;
435+
431436
// Initialise the cache to contain grid information that is constant
432437
// across the lifetime of this object, i.e. does not vary between calls to
433438
// create_contour() and create_filled_contour().

0 commit comments

Comments
 (0)