Skip to content

Commit b7ba9e4

Browse files
authored
Merge pull request #17885 from brunobeltran/ignore_closepoly_after_nan
BF: ignore CLOSEPOLY after NaN in PathNanRemover
2 parents b975b75 + e216e59 commit b7ba9e4

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

lib/matplotlib/tests/test_axes.py

+5
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,11 @@ def test_bar_tick_label_single():
14041404
ax.bar("a", "b", align='edge', tick_label='0', data=data)
14051405

14061406

1407+
def test_nan_bar_values():
1408+
fig, ax = plt.subplots()
1409+
ax.bar([0, 1], [np.nan, 4])
1410+
1411+
14071412
def test_bar_ticklabel_fail():
14081413
fig, ax = plt.subplots()
14091414
ax.bar([], [])

lib/matplotlib/tests/test_path.py

+23
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,26 @@ def test_intersect_zero_length_segment():
452452

453453
assert outline_path.intersects_path(this_path)
454454
assert this_path.intersects_path(outline_path)
455+
456+
457+
def test_cleanup_closepoly():
458+
# if the first connected component of a Path ends in a CLOSEPOLY, but that
459+
# component contains a NaN, then Path.cleaned should ignore not just the
460+
# control points but also the CLOSEPOLY, since it has nowhere valid to
461+
# point.
462+
paths = [
463+
Path([[np.nan, np.nan], [np.nan, np.nan]],
464+
[Path.MOVETO, Path.CLOSEPOLY]),
465+
# we trigger a different path in the C++ code if we don't pass any
466+
# codes explicitly, so we must also make sure that this works
467+
Path([[np.nan, np.nan], [np.nan, np.nan]]),
468+
# we should also make sure that this cleanup works if there's some
469+
# multi-vertex curves
470+
Path([[np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan],
471+
[np.nan, np.nan]],
472+
[Path.MOVETO, Path.CURVE3, Path.CURVE3, Path.CLOSEPOLY])
473+
]
474+
for p in paths:
475+
cleaned = p.cleaned(remove_nans=True)
476+
assert len(cleaned) == 1
477+
assert cleaned.codes[0] == Path.STOP

src/path_converters.h

+16-5
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ class PathNanRemover : protected EmbeddedQueue<4>
163163
VertexSource *m_source;
164164
bool m_remove_nans;
165165
bool m_has_curves;
166+
bool valid_segment_exists;
166167

167168
public:
168169
/* has_curves should be true if the path contains bezier curve
@@ -172,7 +173,9 @@ class PathNanRemover : protected EmbeddedQueue<4>
172173
PathNanRemover(VertexSource &source, bool remove_nans, bool has_curves)
173174
: m_source(&source), m_remove_nans(remove_nans), m_has_curves(has_curves)
174175
{
175-
// empty
176+
// ignore all close/end_poly commands until after the first valid
177+
// (nan-free) command is encountered
178+
valid_segment_exists = false;
176179
}
177180

178181
inline void rewind(unsigned path_id)
@@ -202,8 +205,13 @@ class PathNanRemover : protected EmbeddedQueue<4>
202205
are found along the way, the queue is emptied, and
203206
the next curve segment is handled. */
204207
code = m_source->vertex(x, y);
208+
/* The vertices attached to STOP and CLOSEPOLY left are never
209+
used, so we leave them as-is even if NaN. However, CLOSEPOLY
210+
only makes sense if a valid MOVETO command has already been
211+
emitted. */
205212
if (code == agg::path_cmd_stop ||
206-
code == (agg::path_cmd_end_poly | agg::path_flags_close)) {
213+
(code == (agg::path_cmd_end_poly | agg::path_flags_close) &&
214+
valid_segment_exists)) {
207215
return code;
208216
}
209217

@@ -224,6 +232,7 @@ class PathNanRemover : protected EmbeddedQueue<4>
224232
}
225233

226234
if (!has_nan) {
235+
valid_segment_exists = true;
227236
break;
228237
}
229238

@@ -251,21 +260,23 @@ class PathNanRemover : protected EmbeddedQueue<4>
251260
code = m_source->vertex(x, y);
252261

253262
if (code == agg::path_cmd_stop ||
254-
code == (agg::path_cmd_end_poly | agg::path_flags_close)) {
263+
(code == (agg::path_cmd_end_poly | agg::path_flags_close) &&
264+
valid_segment_exists)) {
255265
return code;
256266
}
257267

258268
if (!(std::isfinite(*x) && std::isfinite(*y))) {
259269
do {
260270
code = m_source->vertex(x, y);
261271
if (code == agg::path_cmd_stop ||
262-
code == (agg::path_cmd_end_poly | agg::path_flags_close)) {
272+
(code == (agg::path_cmd_end_poly | agg::path_flags_close) &&
273+
valid_segment_exists)) {
263274
return code;
264275
}
265276
} while (!(std::isfinite(*x) && std::isfinite(*y)));
266277
return agg::path_cmd_move_to;
267278
}
268-
279+
valid_segment_exists = true;
269280
return code;
270281
}
271282
}

0 commit comments

Comments
 (0)