_path_wrapper.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. #include "numpy_cpp.h"
  2. #include "_path.h"
  3. #include "py_converters.h"
  4. #include "py_adaptors.h"
  5. PyObject *convert_polygon_vector(std::vector<Polygon> &polygons)
  6. {
  7. PyObject *pyresult = PyList_New(polygons.size());
  8. for (size_t i = 0; i < polygons.size(); ++i) {
  9. Polygon poly = polygons[i];
  10. npy_intp dims[2];
  11. dims[1] = 2;
  12. dims[0] = (npy_intp)poly.size();
  13. numpy::array_view<double, 2> subresult(dims);
  14. memcpy(subresult.data(), &poly[0], sizeof(double) * poly.size() * 2);
  15. if (PyList_SetItem(pyresult, i, subresult.pyobj())) {
  16. Py_DECREF(pyresult);
  17. return NULL;
  18. }
  19. }
  20. return pyresult;
  21. }
  22. const char *Py_point_in_path__doc__ =
  23. "point_in_path(x, y, radius, path, trans)\n"
  24. "--\n\n";
  25. static PyObject *Py_point_in_path(PyObject *self, PyObject *args)
  26. {
  27. double x, y, r;
  28. py::PathIterator path;
  29. agg::trans_affine trans;
  30. bool result;
  31. if (!PyArg_ParseTuple(args,
  32. "dddO&O&:point_in_path",
  33. &x,
  34. &y,
  35. &r,
  36. &convert_path,
  37. &path,
  38. &convert_trans_affine,
  39. &trans)) {
  40. return NULL;
  41. }
  42. CALL_CPP("point_in_path", (result = point_in_path(x, y, r, path, trans)));
  43. if (result) {
  44. Py_RETURN_TRUE;
  45. } else {
  46. Py_RETURN_FALSE;
  47. }
  48. }
  49. const char *Py_points_in_path__doc__ =
  50. "points_in_path(points, radius, path, trans)\n"
  51. "--\n\n";
  52. static PyObject *Py_points_in_path(PyObject *self, PyObject *args)
  53. {
  54. numpy::array_view<const double, 2> points;
  55. double r;
  56. py::PathIterator path;
  57. agg::trans_affine trans;
  58. if (!PyArg_ParseTuple(args,
  59. "O&dO&O&:points_in_path",
  60. &convert_points,
  61. &points,
  62. &r,
  63. &convert_path,
  64. &path,
  65. &convert_trans_affine,
  66. &trans)) {
  67. return NULL;
  68. }
  69. npy_intp dims[] = { (npy_intp)points.size() };
  70. numpy::array_view<uint8_t, 1> results(dims);
  71. CALL_CPP("points_in_path", (points_in_path(points, r, path, trans, results)));
  72. return results.pyobj();
  73. }
  74. const char *Py_update_path_extents__doc__ =
  75. "update_path_extents(path, trans, rect, minpos, ignore)\n"
  76. "--\n\n";
  77. static PyObject *Py_update_path_extents(PyObject *self, PyObject *args)
  78. {
  79. py::PathIterator path;
  80. agg::trans_affine trans;
  81. agg::rect_d rect;
  82. numpy::array_view<double, 1> minpos;
  83. int ignore;
  84. int changed;
  85. if (!PyArg_ParseTuple(args,
  86. "O&O&O&O&i:update_path_extents",
  87. &convert_path,
  88. &path,
  89. &convert_trans_affine,
  90. &trans,
  91. &convert_rect,
  92. &rect,
  93. &minpos.converter,
  94. &minpos,
  95. &ignore)) {
  96. return NULL;
  97. }
  98. if (minpos.dim(0) != 2) {
  99. PyErr_Format(PyExc_ValueError,
  100. "minpos must be of length 2, got %" NPY_INTP_FMT,
  101. minpos.dim(0));
  102. return NULL;
  103. }
  104. extent_limits e;
  105. if (ignore) {
  106. CALL_CPP("update_path_extents", reset_limits(e));
  107. } else {
  108. if (rect.x1 > rect.x2) {
  109. e.x0 = std::numeric_limits<double>::infinity();
  110. e.x1 = -std::numeric_limits<double>::infinity();
  111. } else {
  112. e.x0 = rect.x1;
  113. e.x1 = rect.x2;
  114. }
  115. if (rect.y1 > rect.y2) {
  116. e.y0 = std::numeric_limits<double>::infinity();
  117. e.y1 = -std::numeric_limits<double>::infinity();
  118. } else {
  119. e.y0 = rect.y1;
  120. e.y1 = rect.y2;
  121. }
  122. e.xm = minpos(0);
  123. e.ym = minpos(1);
  124. }
  125. CALL_CPP("update_path_extents", (update_path_extents(path, trans, e)));
  126. changed = (e.x0 != rect.x1 || e.y0 != rect.y1 || e.x1 != rect.x2 || e.y1 != rect.y2 ||
  127. e.xm != minpos(0) || e.ym != minpos(1));
  128. npy_intp extentsdims[] = { 2, 2 };
  129. numpy::array_view<double, 2> outextents(extentsdims);
  130. outextents(0, 0) = e.x0;
  131. outextents(0, 1) = e.y0;
  132. outextents(1, 0) = e.x1;
  133. outextents(1, 1) = e.y1;
  134. npy_intp minposdims[] = { 2 };
  135. numpy::array_view<double, 1> outminpos(minposdims);
  136. outminpos(0) = e.xm;
  137. outminpos(1) = e.ym;
  138. return Py_BuildValue(
  139. "NNi", outextents.pyobj(), outminpos.pyobj(), changed);
  140. }
  141. const char *Py_get_path_collection_extents__doc__ =
  142. "get_path_collection_extents("
  143. "master_transform, paths, transforms, offsets, offset_transform)\n"
  144. "--\n\n";
  145. static PyObject *Py_get_path_collection_extents(PyObject *self, PyObject *args)
  146. {
  147. agg::trans_affine master_transform;
  148. py::PathGenerator paths;
  149. numpy::array_view<const double, 3> transforms;
  150. numpy::array_view<const double, 2> offsets;
  151. agg::trans_affine offset_trans;
  152. extent_limits e;
  153. if (!PyArg_ParseTuple(args,
  154. "O&O&O&O&O&:get_path_collection_extents",
  155. &convert_trans_affine,
  156. &master_transform,
  157. &convert_pathgen,
  158. &paths,
  159. &convert_transforms,
  160. &transforms,
  161. &convert_points,
  162. &offsets,
  163. &convert_trans_affine,
  164. &offset_trans)) {
  165. return NULL;
  166. }
  167. CALL_CPP("get_path_collection_extents",
  168. (get_path_collection_extents(
  169. master_transform, paths, transforms, offsets, offset_trans, e)));
  170. npy_intp dims[] = { 2, 2 };
  171. numpy::array_view<double, 2> extents(dims);
  172. extents(0, 0) = e.x0;
  173. extents(0, 1) = e.y0;
  174. extents(1, 0) = e.x1;
  175. extents(1, 1) = e.y1;
  176. npy_intp minposdims[] = { 2 };
  177. numpy::array_view<double, 1> minpos(minposdims);
  178. minpos(0) = e.xm;
  179. minpos(1) = e.ym;
  180. return Py_BuildValue("NN", extents.pyobj(), minpos.pyobj());
  181. }
  182. const char *Py_point_in_path_collection__doc__ =
  183. "point_in_path_collection("
  184. "x, y, radius, master_transform, paths, transforms, offsets, "
  185. "offset_trans, filled)\n"
  186. "--\n\n";
  187. static PyObject *Py_point_in_path_collection(PyObject *self, PyObject *args)
  188. {
  189. double x, y, radius;
  190. agg::trans_affine master_transform;
  191. py::PathGenerator paths;
  192. numpy::array_view<const double, 3> transforms;
  193. numpy::array_view<const double, 2> offsets;
  194. agg::trans_affine offset_trans;
  195. bool filled;
  196. std::vector<int> result;
  197. if (!PyArg_ParseTuple(args,
  198. "dddO&O&O&O&O&O&:point_in_path_collection",
  199. &x,
  200. &y,
  201. &radius,
  202. &convert_trans_affine,
  203. &master_transform,
  204. &convert_pathgen,
  205. &paths,
  206. &convert_transforms,
  207. &transforms,
  208. &convert_points,
  209. &offsets,
  210. &convert_trans_affine,
  211. &offset_trans,
  212. &convert_bool,
  213. &filled)) {
  214. return NULL;
  215. }
  216. CALL_CPP("point_in_path_collection",
  217. (point_in_path_collection(x,
  218. y,
  219. radius,
  220. master_transform,
  221. paths,
  222. transforms,
  223. offsets,
  224. offset_trans,
  225. filled,
  226. result)));
  227. npy_intp dims[] = {(npy_intp)result.size() };
  228. numpy::array_view<int, 1> pyresult(dims);
  229. if (result.size() > 0) {
  230. memcpy(pyresult.data(), &result[0], result.size() * sizeof(int));
  231. }
  232. return pyresult.pyobj();
  233. }
  234. const char *Py_path_in_path__doc__ =
  235. "path_in_path(path_a, trans_a, path_b, trans_b)\n"
  236. "--\n\n";
  237. static PyObject *Py_path_in_path(PyObject *self, PyObject *args)
  238. {
  239. py::PathIterator a;
  240. agg::trans_affine atrans;
  241. py::PathIterator b;
  242. agg::trans_affine btrans;
  243. bool result;
  244. if (!PyArg_ParseTuple(args,
  245. "O&O&O&O&:path_in_path",
  246. &convert_path,
  247. &a,
  248. &convert_trans_affine,
  249. &atrans,
  250. &convert_path,
  251. &b,
  252. &convert_trans_affine,
  253. &btrans)) {
  254. return NULL;
  255. }
  256. CALL_CPP("path_in_path", (result = path_in_path(a, atrans, b, btrans)));
  257. if (result) {
  258. Py_RETURN_TRUE;
  259. } else {
  260. Py_RETURN_FALSE;
  261. }
  262. }
  263. const char *Py_clip_path_to_rect__doc__ =
  264. "clip_path_to_rect(path, rect, inside)\n"
  265. "--\n\n";
  266. static PyObject *Py_clip_path_to_rect(PyObject *self, PyObject *args)
  267. {
  268. py::PathIterator path;
  269. agg::rect_d rect;
  270. bool inside;
  271. std::vector<Polygon> result;
  272. if (!PyArg_ParseTuple(args,
  273. "O&O&O&:clip_path_to_rect",
  274. &convert_path,
  275. &path,
  276. &convert_rect,
  277. &rect,
  278. &convert_bool,
  279. &inside)) {
  280. return NULL;
  281. }
  282. CALL_CPP("clip_path_to_rect", (clip_path_to_rect(path, rect, inside, result)));
  283. return convert_polygon_vector(result);
  284. }
  285. const char *Py_affine_transform__doc__ =
  286. "affine_transform(points, trans)\n"
  287. "--\n\n";
  288. static PyObject *Py_affine_transform(PyObject *self, PyObject *args)
  289. {
  290. PyObject *vertices_obj;
  291. agg::trans_affine trans;
  292. if (!PyArg_ParseTuple(args,
  293. "OO&:affine_transform",
  294. &vertices_obj,
  295. &convert_trans_affine,
  296. &trans)) {
  297. return NULL;
  298. }
  299. PyArrayObject* vertices_arr = (PyArrayObject *)PyArray_ContiguousFromAny(vertices_obj, NPY_DOUBLE, 1, 2);
  300. if (vertices_arr == NULL) {
  301. return NULL;
  302. }
  303. if (PyArray_NDIM(vertices_arr) == 2) {
  304. numpy::array_view<double, 2> vertices(vertices_arr);
  305. Py_DECREF(vertices_arr);
  306. npy_intp dims[] = { (npy_intp)vertices.size(), 2 };
  307. numpy::array_view<double, 2> result(dims);
  308. CALL_CPP("affine_transform", (affine_transform_2d(vertices, trans, result)));
  309. return result.pyobj();
  310. } else { // PyArray_NDIM(vertices_arr) == 1
  311. numpy::array_view<double, 1> vertices(vertices_arr);
  312. Py_DECREF(vertices_arr);
  313. npy_intp dims[] = { (npy_intp)vertices.size() };
  314. numpy::array_view<double, 1> result(dims);
  315. CALL_CPP("affine_transform", (affine_transform_1d(vertices, trans, result)));
  316. return result.pyobj();
  317. }
  318. }
  319. const char *Py_count_bboxes_overlapping_bbox__doc__ =
  320. "count_bboxes_overlapping_bbox(bbox, bboxes)\n"
  321. "--\n\n";
  322. static PyObject *Py_count_bboxes_overlapping_bbox(PyObject *self, PyObject *args)
  323. {
  324. agg::rect_d bbox;
  325. numpy::array_view<const double, 3> bboxes;
  326. int result;
  327. if (!PyArg_ParseTuple(args,
  328. "O&O&:count_bboxes_overlapping_bbox",
  329. &convert_rect,
  330. &bbox,
  331. &convert_bboxes,
  332. &bboxes)) {
  333. return NULL;
  334. }
  335. CALL_CPP("count_bboxes_overlapping_bbox",
  336. (result = count_bboxes_overlapping_bbox(bbox, bboxes)));
  337. return PyLong_FromLong(result);
  338. }
  339. const char *Py_path_intersects_path__doc__ =
  340. "path_intersects_path(path1, path2, filled=False)\n"
  341. "--\n\n";
  342. static PyObject *Py_path_intersects_path(PyObject *self, PyObject *args, PyObject *kwds)
  343. {
  344. py::PathIterator p1;
  345. py::PathIterator p2;
  346. agg::trans_affine t1;
  347. agg::trans_affine t2;
  348. int filled = 0;
  349. const char *names[] = { "p1", "p2", "filled", NULL };
  350. bool result;
  351. if (!PyArg_ParseTupleAndKeywords(args,
  352. kwds,
  353. "O&O&i:path_intersects_path",
  354. (char **)names,
  355. &convert_path,
  356. &p1,
  357. &convert_path,
  358. &p2,
  359. &filled)) {
  360. return NULL;
  361. }
  362. CALL_CPP("path_intersects_path", (result = path_intersects_path(p1, p2)));
  363. if (filled) {
  364. if (!result) {
  365. CALL_CPP("path_intersects_path",
  366. (result = path_in_path(p1, t1, p2, t2)));
  367. }
  368. if (!result) {
  369. CALL_CPP("path_intersects_path",
  370. (result = path_in_path(p2, t1, p1, t2)));
  371. }
  372. }
  373. if (result) {
  374. Py_RETURN_TRUE;
  375. } else {
  376. Py_RETURN_FALSE;
  377. }
  378. }
  379. const char *Py_path_intersects_rectangle__doc__ =
  380. "path_intersects_rectangle("
  381. "path, rect_x1, rect_y1, rect_x2, rect_y2, filled=False)\n"
  382. "--\n\n";
  383. static PyObject *Py_path_intersects_rectangle(PyObject *self, PyObject *args, PyObject *kwds)
  384. {
  385. py::PathIterator path;
  386. double rect_x1, rect_y1, rect_x2, rect_y2;
  387. bool filled = false;
  388. const char *names[] = { "path", "rect_x1", "rect_y1", "rect_x2", "rect_y2", "filled", NULL };
  389. bool result;
  390. if (!PyArg_ParseTupleAndKeywords(args,
  391. kwds,
  392. "O&dddd|O&:path_intersects_rectangle",
  393. (char **)names,
  394. &convert_path,
  395. &path,
  396. &rect_x1,
  397. &rect_y1,
  398. &rect_x2,
  399. &rect_y2,
  400. &convert_bool,
  401. &filled)) {
  402. return NULL;
  403. }
  404. CALL_CPP("path_intersects_rectangle", (result = path_intersects_rectangle(path, rect_x1, rect_y1, rect_x2, rect_y2, filled)));
  405. if (result) {
  406. Py_RETURN_TRUE;
  407. } else {
  408. Py_RETURN_FALSE;
  409. }
  410. }
  411. const char *Py_convert_path_to_polygons__doc__ =
  412. "convert_path_to_polygons(path, trans, width=0, height=0)\n"
  413. "--\n\n";
  414. static PyObject *Py_convert_path_to_polygons(PyObject *self, PyObject *args, PyObject *kwds)
  415. {
  416. py::PathIterator path;
  417. agg::trans_affine trans;
  418. double width = 0.0, height = 0.0;
  419. int closed_only = 1;
  420. std::vector<Polygon> result;
  421. const char *names[] = { "path", "transform", "width", "height", "closed_only", NULL };
  422. if (!PyArg_ParseTupleAndKeywords(args,
  423. kwds,
  424. "O&O&|ddi:convert_path_to_polygons",
  425. (char **)names,
  426. &convert_path,
  427. &path,
  428. &convert_trans_affine,
  429. &trans,
  430. &width,
  431. &height,
  432. &closed_only)) {
  433. return NULL;
  434. }
  435. CALL_CPP("convert_path_to_polygons",
  436. (convert_path_to_polygons(path, trans, width, height, closed_only, result)));
  437. return convert_polygon_vector(result);
  438. }
  439. const char *Py_cleanup_path__doc__ =
  440. "cleanup_path("
  441. "path, trans, remove_nans, clip_rect, snap_mode, stroke_width, simplify, "
  442. "return_curves, sketch)\n"
  443. "--\n\n";
  444. static PyObject *Py_cleanup_path(PyObject *self, PyObject *args)
  445. {
  446. py::PathIterator path;
  447. agg::trans_affine trans;
  448. bool remove_nans;
  449. agg::rect_d clip_rect;
  450. e_snap_mode snap_mode;
  451. double stroke_width;
  452. PyObject *simplifyobj;
  453. bool simplify = false;
  454. bool return_curves;
  455. SketchParams sketch;
  456. if (!PyArg_ParseTuple(args,
  457. "O&O&O&O&O&dOO&O&:cleanup_path",
  458. &convert_path,
  459. &path,
  460. &convert_trans_affine,
  461. &trans,
  462. &convert_bool,
  463. &remove_nans,
  464. &convert_rect,
  465. &clip_rect,
  466. &convert_snap,
  467. &snap_mode,
  468. &stroke_width,
  469. &simplifyobj,
  470. &convert_bool,
  471. &return_curves,
  472. &convert_sketch_params,
  473. &sketch)) {
  474. return NULL;
  475. }
  476. if (simplifyobj == Py_None) {
  477. simplify = path.should_simplify();
  478. } else {
  479. switch (PyObject_IsTrue(simplifyobj)) {
  480. case 0: simplify = false; break;
  481. case 1: simplify = true; break;
  482. default: return NULL; // errored.
  483. }
  484. }
  485. bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2);
  486. std::vector<double> vertices;
  487. std::vector<npy_uint8> codes;
  488. CALL_CPP("cleanup_path",
  489. (cleanup_path(path,
  490. trans,
  491. remove_nans,
  492. do_clip,
  493. clip_rect,
  494. snap_mode,
  495. stroke_width,
  496. simplify,
  497. return_curves,
  498. sketch,
  499. vertices,
  500. codes)));
  501. size_t length = codes.size();
  502. npy_intp vertices_dims[] = {(npy_intp)length, 2 };
  503. numpy::array_view<double, 2> pyvertices(vertices_dims);
  504. npy_intp codes_dims[] = {(npy_intp)length };
  505. numpy::array_view<unsigned char, 1> pycodes(codes_dims);
  506. memcpy(pyvertices.data(), &vertices[0], sizeof(double) * 2 * length);
  507. memcpy(pycodes.data(), &codes[0], sizeof(unsigned char) * length);
  508. return Py_BuildValue("NN", pyvertices.pyobj(), pycodes.pyobj());
  509. }
  510. const char *Py_convert_to_string__doc__ =
  511. "convert_to_string("
  512. "path, trans, clip_rect, simplify, sketch, precision, codes, postfix)\n"
  513. "--\n\n"
  514. "Convert *path* to a bytestring.\n"
  515. "\n"
  516. "The first five parameters (up to *sketch*) are interpreted as in\n"
  517. "`.cleanup_path`. The following ones are detailed below.\n"
  518. "\n"
  519. "Parameters\n"
  520. "----------\n"
  521. "path : Path\n"
  522. "trans : Transform or None\n"
  523. "clip_rect : sequence of 4 floats, or None\n"
  524. "simplify : bool\n"
  525. "sketch : tuple of 3 floats, or None\n"
  526. "precision : int\n"
  527. " The precision used to \"%.*f\"-format the values. Trailing zeros\n"
  528. " and decimal points are always removed. (precision=-1 is a special\n"
  529. " case used to implement ttconv-back-compatible conversion.)\n"
  530. "codes : sequence of 5 bytestrings\n"
  531. " The bytes representation of each opcode (MOVETO, LINETO, CURVE3,\n"
  532. " CURVE4, CLOSEPOLY), in that order. If the bytes for CURVE3 is\n"
  533. " empty, quad segments are automatically converted to cubic ones\n"
  534. " (this is used by backends such as pdf and ps, which do not support\n"
  535. " quads).\n"
  536. "postfix : bool\n"
  537. " Whether the opcode comes after the values (True) or before (False).\n"
  538. ;
  539. static PyObject *Py_convert_to_string(PyObject *self, PyObject *args)
  540. {
  541. py::PathIterator path;
  542. agg::trans_affine trans;
  543. agg::rect_d cliprect;
  544. PyObject *simplifyobj;
  545. bool simplify = false;
  546. SketchParams sketch;
  547. int precision;
  548. char *codes[5];
  549. bool postfix;
  550. std::string buffer;
  551. bool status;
  552. if (!PyArg_ParseTuple(args,
  553. "O&O&O&OO&i(yyyyy)O&:convert_to_string",
  554. &convert_path,
  555. &path,
  556. &convert_trans_affine,
  557. &trans,
  558. &convert_rect,
  559. &cliprect,
  560. &simplifyobj,
  561. &convert_sketch_params,
  562. &sketch,
  563. &precision,
  564. &codes[0],
  565. &codes[1],
  566. &codes[2],
  567. &codes[3],
  568. &codes[4],
  569. &convert_bool,
  570. &postfix)) {
  571. return NULL;
  572. }
  573. if (simplifyobj == Py_None) {
  574. simplify = path.should_simplify();
  575. } else {
  576. switch (PyObject_IsTrue(simplifyobj)) {
  577. case 0: simplify = false; break;
  578. case 1: simplify = true; break;
  579. default: return NULL; // errored.
  580. }
  581. }
  582. CALL_CPP("convert_to_string",
  583. (status = convert_to_string(
  584. path, trans, cliprect, simplify, sketch,
  585. precision, codes, postfix, buffer)));
  586. if (!status) {
  587. PyErr_SetString(PyExc_ValueError, "Malformed path codes");
  588. return NULL;
  589. }
  590. return PyBytes_FromStringAndSize(buffer.c_str(), buffer.size());
  591. }
  592. const char *Py_is_sorted_and_has_non_nan__doc__ =
  593. "is_sorted_and_has_non_nan(array, /)\n"
  594. "--\n\n"
  595. "Return whether the 1D *array* is monotonically increasing, ignoring NaNs,\n"
  596. "and has at least one non-nan value.";
  597. static PyObject *Py_is_sorted_and_has_non_nan(PyObject *self, PyObject *obj)
  598. {
  599. bool result;
  600. PyArrayObject *array = (PyArrayObject *)PyArray_FromAny(
  601. obj, NULL, 1, 1, 0, NULL);
  602. if (array == NULL) {
  603. return NULL;
  604. }
  605. /* Handle just the most common types here, otherwise coerce to double */
  606. switch (PyArray_TYPE(array)) {
  607. case NPY_INT:
  608. result = is_sorted_and_has_non_nan<npy_int>(array);
  609. break;
  610. case NPY_LONG:
  611. result = is_sorted_and_has_non_nan<npy_long>(array);
  612. break;
  613. case NPY_LONGLONG:
  614. result = is_sorted_and_has_non_nan<npy_longlong>(array);
  615. break;
  616. case NPY_FLOAT:
  617. result = is_sorted_and_has_non_nan<npy_float>(array);
  618. break;
  619. case NPY_DOUBLE:
  620. result = is_sorted_and_has_non_nan<npy_double>(array);
  621. break;
  622. default:
  623. Py_DECREF(array);
  624. array = (PyArrayObject *)PyArray_FromObject(obj, NPY_DOUBLE, 1, 1);
  625. if (array == NULL) {
  626. return NULL;
  627. }
  628. result = is_sorted_and_has_non_nan<npy_double>(array);
  629. }
  630. Py_DECREF(array);
  631. if (result) {
  632. Py_RETURN_TRUE;
  633. } else {
  634. Py_RETURN_FALSE;
  635. }
  636. }
  637. static PyMethodDef module_functions[] = {
  638. {"point_in_path", (PyCFunction)Py_point_in_path, METH_VARARGS, Py_point_in_path__doc__},
  639. {"points_in_path", (PyCFunction)Py_points_in_path, METH_VARARGS, Py_points_in_path__doc__},
  640. {"update_path_extents", (PyCFunction)Py_update_path_extents, METH_VARARGS, Py_update_path_extents__doc__},
  641. {"get_path_collection_extents", (PyCFunction)Py_get_path_collection_extents, METH_VARARGS, Py_get_path_collection_extents__doc__},
  642. {"point_in_path_collection", (PyCFunction)Py_point_in_path_collection, METH_VARARGS, Py_point_in_path_collection__doc__},
  643. {"path_in_path", (PyCFunction)Py_path_in_path, METH_VARARGS, Py_path_in_path__doc__},
  644. {"clip_path_to_rect", (PyCFunction)Py_clip_path_to_rect, METH_VARARGS, Py_clip_path_to_rect__doc__},
  645. {"affine_transform", (PyCFunction)Py_affine_transform, METH_VARARGS, Py_affine_transform__doc__},
  646. {"count_bboxes_overlapping_bbox", (PyCFunction)Py_count_bboxes_overlapping_bbox, METH_VARARGS, Py_count_bboxes_overlapping_bbox__doc__},
  647. {"path_intersects_path", (PyCFunction)Py_path_intersects_path, METH_VARARGS|METH_KEYWORDS, Py_path_intersects_path__doc__},
  648. {"path_intersects_rectangle", (PyCFunction)Py_path_intersects_rectangle, METH_VARARGS|METH_KEYWORDS, Py_path_intersects_rectangle__doc__},
  649. {"convert_path_to_polygons", (PyCFunction)Py_convert_path_to_polygons, METH_VARARGS|METH_KEYWORDS, Py_convert_path_to_polygons__doc__},
  650. {"cleanup_path", (PyCFunction)Py_cleanup_path, METH_VARARGS, Py_cleanup_path__doc__},
  651. {"convert_to_string", (PyCFunction)Py_convert_to_string, METH_VARARGS, Py_convert_to_string__doc__},
  652. {"is_sorted_and_has_non_nan", (PyCFunction)Py_is_sorted_and_has_non_nan, METH_O, Py_is_sorted_and_has_non_nan__doc__},
  653. {NULL}
  654. };
  655. static struct PyModuleDef moduledef = {
  656. PyModuleDef_HEAD_INIT, "_path", NULL, 0, module_functions
  657. };
  658. PyMODINIT_FUNC PyInit__path(void)
  659. {
  660. import_array();
  661. return PyModule_Create(&moduledef);
  662. }