8000 Merge branch 'v1.5.x' into v2.x · matplotlib/matplotlib@dc258b3 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit dc258b3

Browse files
committed
Merge branch 'v1.5.x' into v2.x
Conflicts: lib/matplotlib/lines.py conflicts in the imports, resolved to only add _path import lib/matplotlib/tests/test_lines.py resolve to make minimal changes to test function
2 parents d041565 + db80928 commit dc258b3

File tree

4 files changed

+159
-6
lines changed

4 files changed

+159
-6
lines changed

lib/matplotlib/lines.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN,
3535
CARETLEFTBASE, CARETRIGHTBASE, CARETUPBASE, CARETDOWNBASE)
3636

37+
from matplotlib import _path
38+
3739

3840
def segment_hits(cx, cy, x, y, radius):
3941
"""
@@ -702,9 +704,7 @@ def set_transform(self, t):
702704
def _is_sorted(self, x):
703705
"""return True if x is sorted in ascending order"""
704706
# We don't handle the monotonically decreasing case.
705-
if len(x) < 2:
706-
return True
707-
return np.nanmin(x[1:] - x[:-1]) >= 0
707+
return _path.is_sorted(x)
708708

709709
@allow_rasterization
710710
def draw(self, renderer):

lib/matplotlib/tests/test_lines.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,11 @@ def test_marker_fill_styles():
156156

157157

158158
def test_nan_is_sorted():
159-
# Exercises issue from PR #2744 (NaN throwing warning in _is_sorted)
160159
line = mlines.Line2D([],[])
161-
assert_true(line._is_sorted(np.array([1,2,3])))
162-
assert_true(not line._is_sorted(np.array([1,np.nan,3])))
160+
assert_true(line._is_sorted(np.array([1, 2, 3])))
161+
assert_true(line._is_sorted(np.array([1, np.nan, 3])))
162+
assert_true(not line._is_sorted([3, 5] + [np.nan] * 100 + [0, 2]))
163+
163164

164165

165166
if __name__ == '__main__':

src/_path.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,4 +1183,70 @@ int convert_to_string(PathIterator &path,
11831183

11841184
}
11851185

1186+
template<class T>
1187+
struct _is_sorted
1188+
{
1189+
bool operator()(PyArrayObject *array)
1190+
{
1191+
npy_intp size;
1192+
npy_intp i;
1193+
T last_value;
1194+
T current_value;
1195+
1196+
size = PyArray_DIM(array, 0);
1197+
1198+
for (i = 0; i < size; ++i) {
1199+
last_value = *((T *)PyArray_GETPTR1(array, i));
1200+
if (std::isfinite(last_value)) {
1201+
break;
1202+
}
1203+
}
1204+
1205+
if (i == size) {
1206+
// The whole array is non-finite
1207+
return false;
1208+
}
1209+
1210+
for (; i < size; ++i) {
1211+
current_value = *((T *)PyArray_GETPTR1(array, i));
1212+
if (std::isfinite(current_value)) {
1213+
if (current_value < last_value) {
1214+
return false;
1215+
}
1216+
last_value = current_value;
1217+
}
1218+
}
1219+
1220+
return true;
1221+
}
1222+
};
1223+
1224+
1225+
template<class T>
1226+
struct _is_sorted_int
1227+
{
1228+
bool operator()(PyArrayObject *array)
1229+
{
1230+
npy_intp size;
1231+
npy_intp i;
1232+
T last_value;
1233+
T current_value;
1234+
1235+
size = PyArray_DIM(array, 0);
1236+
1237+
last_value = *((T *)PyArray_GETPTR1(array, 0));
1238+
1239+
for (i = 1; i < size; ++i) {
1240+
current_value = *((T *)PyArray_GETPTR1(array, i));
1241+
if (current_value < last_value) {
1242+
return false;
1243+
}
1244+
last_value = current_value;
1245+
}
1246+
1247+
return true;
1248+
}
1249+
};
1250+
1251+
11861252
#endif

src/_path_wrapper.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,91 @@ static PyObject *Py_convert_to_string(PyObject *self, PyObject *args, PyObject *
726726
return result;
727727
}
728728

729+
730+
const char *Py_is_sorted__doc__ = "is_sorted(array)\n\n"
731+
"Returns True if 1-D array is monotonically increasing, ignoring NaNs\n";
732+
733+
static PyObject *Py_is_sorted(PyObject *self, PyObject *obj)
734+
{
735+
npy_intp size;
736+
bool result;
737+
738+
PyArrayObject *array = (PyArrayObject *)PyArray_FromAny(
739+
obj, NULL, 1, 1, 0, NULL);
740+
741+
if (array == NULL) {
742+
return NULL;
743+
}
744+
745+
size = PyArray_DIM(array, 0);
746+
747+
if (size < 2) {
748+
Py_DECREF(array);
749+
Py_RETURN_TRUE;
750+
}
751+
752+
/* Handle just the most common types here, otherwise coerce to
753+
double */
754+
switch(PyArray_TYPE(array)) {
755+
case NPY_INT:
756+
{
757+
_is_sorted_int<npy_int> is_sorted;
758+
result = is_sorted(array);
759+
}
760+
break;
761+
762+
case NPY_LONG:
763+
{
764+
_is_sorted_int<npy_long> is_sorted;
765+
result = is_sorted(array);
766+
}
767+
break;
768+
769+
case NPY_LONGLONG:
770+
{
771+
_is_sorted_int<npy_longlong> is_sorted;
772+
result = is_sorted(array);
773+
}
774+
break;
775+
776+
case NPY_FLOAT:
777+
{
778+
_is_sorted<npy_float> is_sorted;
779+
result = is_sorted(array);
780+
}
781+
break;
782+
783+
case NPY_DOUBLE:
784+
{
785+
_is_sorted<npy_double> is_sorted;
786+
result = is_sorted(array);
787+
}
788+
break;
789+
790+
default:
791+
{
792+
Py_DECREF(array);
793+
array = (PyArrayObject *)PyArray_FromObject(obj, NPY_DOUBLE, 1, 1);
794+
795+
if (array == NULL) {
796+
return NULL;
797+
}
798+
799+
_is_sorted<npy_double> is_sorted;
800+
result = is_sorted(array);
801+
}
802+
}
803+
804+
Py_DECREF(array);
805+
806+
if (result) {
807+
Py_RETURN_TRUE;
808+
} else {
809+
Py_RETURN_FALSE;
810+
}
811+
}
812+
813+
729814
extern "C" {
730815

731816
static PyMethodDef module_functions[] = {
@@ -745,6 +830,7 @@ extern "C" {
745830
{"convert_path_to_polygons", (PyCFunction)Py_convert_path_to_polygons, METH_VARARGS, Py_convert_path_to_polygons__doc__},
746831
{"cleanup_path", (PyCFunction)Py_cleanup_path, METH_VARARGS, Py_cleanup_path__doc__},
747832
{"convert_to_string", (PyCFunction)Py_convert_to_string, METH_VARARGS, Py_convert_to_string__doc__},
833+
{"is_sorted", (PyCFunction)Py_is_sorted, METH_O, Py_is_sorted__doc__},
748834
{NULL}
749835
};
750836

0 commit comments

Comments
 (0)
0