#include "numpy_cpp.h" #include "_path.h" #include "py_converters.h" #include "py_adaptors.h" PyObject *convert_polygon_vector(std::vector<Polygon> &polygons) { PyObject *pyresult = PyList_New(polygons.size()); for (size_t i = 0; i < polygons.size(); ++i) { Polygon poly = polygons[i]; npy_intp dims[2]; dims[1] = 2; dims[0] = (npy_intp)poly.size(); numpy::array_view<double, 2> subresult(dims); memcpy(subresult.data(), &poly[0], sizeof(double) * poly.size() * 2); if (PyList_SetItem(pyresult, i, subresult.pyobj())) { Py_DECREF(pyresult); return NULL; } } return pyresult; } const char *Py_point_in_path__doc__ = "point_in_path(x, y, radius, path, trans)"; static PyObject *Py_point_in_path(PyObject *self, PyObject *args, PyObject *kwds) { double x, y, r; py::PathIterator path; agg::trans_affine trans; bool result; if (!PyArg_ParseTuple(args, "dddO&O&:point_in_path", &x, &y, &r, &convert_path, &path, &convert_trans_affine, &trans)) { return NULL; } CALL_CPP("point_in_path", (result = point_in_path(x, y, r, path, trans))); if (result) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } const char *Py_points_in_path__doc__ = "points_in_path(points, radius, path, trans)"; static PyObject *Py_points_in_path(PyObject *self, PyObject *args, PyObject *kwds) { numpy::array_view<const double, 2> points; double r; py::PathIterator path; agg::trans_affine trans; if (!PyArg_ParseTuple(args, "O&dO&O&:points_in_path", &convert_points, &points, &r, &convert_path, &path, &convert_trans_affine, &trans)) { return NULL; } npy_intp dims[] = { (npy_intp)points.size() }; numpy::array_view<uint8_t, 1> results(dims); CALL_CPP("points_in_path", (points_in_path(points, r, path, trans, results))); return results.pyobj(); } const char *Py_point_on_path__doc__ = "point_on_path(x, y, radius, path, trans)"; static PyObject *Py_point_on_path(PyObject *self, PyObject *args, PyObject *kwds) { double x, y, r; py::PathIterator path; agg::trans_affine trans; bool result; if (!PyArg_ParseTuple(args, "dddO&O&:point_on_path", &x, &y, &r, &convert_path, &path, &convert_trans_affine, &trans)) { return NULL; } CALL_CPP("point_on_path", (result = point_on_path(x, y, r, path, trans))); if (result) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } const char *Py_points_on_path__doc__ = "points_on_path(points, radius, path, trans)"; static PyObject *Py_points_on_path(PyObject *self, PyObject *args, PyObject *kwds) { numpy::array_view<const double, 2> points; double r; py::PathIterator path; agg::trans_affine trans; if (!PyArg_ParseTuple(args, "O&dO&O&:points_on_path", &convert_points, &points, &r, &convert_path, &path, &convert_trans_affine, &trans)) { return NULL; } npy_intp dims[] = { (npy_intp)points.size() }; numpy::array_view<uint8_t, 1> results(dims); CALL_CPP("points_on_path", (points_on_path(points, r, path, trans, results))); return results.pyobj(); } const char *Py_get_path_extents__doc__ = "get_path_extents(path, trans)"; static PyObject *Py_get_path_extents(PyObject *self, PyObject *args, PyObject *kwds) { py::PathIterator path; agg::trans_affine trans; if (!PyArg_ParseTuple( args, "O&O&:get_path_extents", &convert_path, &path, &convert_trans_affine, &trans)) { return NULL; } extent_limits e; CALL_CPP("get_path_extents", (reset_limits(e))); CALL_CPP("get_path_extents", (update_path_extents(path, trans, e))); npy_intp dims[] = { 2, 2 }; numpy::array_view<double, 2> extents(dims); extents(0, 0) = e.x0; extents(0, 1) = e.y0; extents(1, 0) = e.x1; extents(1, 1) = e.y1; return extents.pyobj(); } const char *Py_update_path_extents__doc__ = "update_path_extents(path, trans, rect, minpos, ignore)"; static PyObject *Py_update_path_extents(PyObject *self, PyObject *args, PyObject *kwds) { py::PathIterator path; agg::trans_affine trans; agg::rect_d rect; numpy::array_view<double, 1> minpos; int ignore; int changed; if (!PyArg_ParseTuple(args, "O&O&O&O&i:update_path_extents", &convert_path, &path, &convert_trans_affine, &trans, &convert_rect, &rect, &minpos.converter, &minpos, &ignore)) { return NULL; } if (minpos.dim(0) != 2) { PyErr_Format(PyExc_ValueError, "minpos must be of length 2, got %" NPY_INTP_FMT, minpos.dim(0)); return NULL; } extent_limits e; if (ignore) { CALL_CPP("update_path_extents", reset_limits(e)); } else { if (rect.x1 > rect.x2) { e.x0 = std::numeric_limits<double>::infinity(); e.x1 = -std::numeric_limits<double>::infinity(); } else { e.x0 = rect.x1; e.x1 = rect.x2; } if (rect.y1 > rect.y2) { e.y0 = std::numeric_limits<double>::infinity(); e.y1 = -std::numeric_limits<double>::infinity(); } else { e.y0 = rect.y1; e.y1 = rect.y2; } e.xm = minpos(0); e.ym = minpos(1); } CALL_CPP("update_path_extents", (update_path_extents(path, trans, e))); changed = (e.x0 != rect.x1 || e.y0 != rect.y1 || e.x1 != rect.x2 || e.y1 != rect.y2 || e.xm != minpos(0) || e.ym != minpos(1)); npy_intp extentsdims[] = { 2, 2 }; numpy::array_view<double, 2> outextents(extentsdims); outextents(0, 0) = e.x0; outextents(0, 1) = e.y0; outextents(1, 0) = e.x1; outextents(1, 1) = e.y1; npy_intp minposdims[] = { 2 }; numpy::array_view<double, 1> outminpos(minposdims); outminpos(0) = e.xm; outminpos(1) = e.ym; return Py_BuildValue( "NNi", outextents.pyobj(), outminpos.pyobj(), changed); } const char *Py_get_path_collection_extents__doc__ = "get_path_collection_extents("; static PyObject *Py_get_path_collection_extents(PyObject *self, PyObject *args, PyObject *kwds) { agg::trans_affine master_transform; PyObject *pathsobj; numpy::array_view<const double, 3> transforms; numpy::array_view<const double, 2> offsets; agg::trans_affine offset_trans; extent_limits e; if (!PyArg_ParseTuple(args, "O&OO&O&O&:get_path_collection_extents", &convert_trans_affine, &master_transform, &pathsobj, &convert_transforms, &transforms, &convert_points, &offsets, &convert_trans_affine, &offset_trans)) { return NULL; } try { py::PathGenerator paths(pathsobj); CALL_CPP("get_path_collection_extents", (get_path_collection_extents( master_transform, paths, transforms, offsets, offset_trans, e))); } catch (const py::exception &) { return NULL; } npy_intp dims[] = { 2, 2 }; numpy::array_view<double, 2> extents(dims); extents(0, 0) = e.x0; extents(0, 1) = e.y0; extents(1, 0) = e.x1; extents(1, 1) = e.y1; return extents.pyobj(); } const char *Py_point_in_path_collection__doc__ = "point_in_path_collection(x, y, radius, master_transform, paths, transforms, offsets, " "offset_trans, filled, offset_position)"; static PyObject *Py_point_in_path_collection(PyObject *self, PyObject *args, PyObject *kwds) { double x, y, radius; agg::trans_affine master_transform; PyObject *pathsobj; numpy::array_view<const double, 3> transforms; numpy::array_view<const double, 2> offsets; agg::trans_affine offset_trans; int filled; e_offset_position offset_position; std::vector<int> result; if (!PyArg_ParseTuple(args, "dddO&OO&O&O&iO&:point_in_path_collection", &x, &y, &radius, &convert_trans_affine, &master_transform, &pathsobj, &convert_transforms, &transforms, &convert_points, &offsets, &convert_trans_affine, &offset_trans, &filled, &convert_offset_position, &offset_position)) { return NULL; } try { py::PathGenerator paths(pathsobj); CALL_CPP("point_in_path_collection", (point_in_path_collection(x, y, radius, master_transform, paths, transforms, offsets, offset_trans, filled, offset_position, result))); } catch (const py::exception &) { return NULL; } npy_intp dims[] = {(npy_intp)result.size() }; numpy::array_view<int, 1> pyresult(dims); if (result.size() > 0) { memcpy(pyresult.data(), &result[0], result.size() * sizeof(int)); } return pyresult.pyobj(); } const char *Py_path_in_path__doc__ = "path_in_path(path_a, trans_a, path_b, trans_b)"; static PyObject *Py_path_in_path(PyObject *self, PyObject *args, PyObject *kwds) { py::PathIterator a; agg::trans_affine atrans; py::PathIterator b; agg::trans_affine btrans; bool result; if (!PyArg_ParseTuple(args, "O&O&O&O&:path_in_path", &convert_path, &a, &convert_trans_affine, &atrans, &convert_path, &b, &convert_trans_affine, &btrans)) { return NULL; } CALL_CPP("path_in_path", (result = path_in_path(a, atrans, b, btrans))); if (result) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } const char *Py_clip_path_to_rect__doc__ = "clip_path_to_rect(path, rect, inside)"; static PyObject *Py_clip_path_to_rect(PyObject *self, PyObject *args, PyObject *kwds) { py::PathIterator path; agg::rect_d rect; int inside; std::vector<Polygon> result; if (!PyArg_ParseTuple(args, "O&O&i:clip_path_to_rect", &convert_path, &path, &convert_rect, &rect, &inside)) { return NULL; } CALL_CPP("clip_path_to_rect", (clip_path_to_rect(path, rect, inside, result))); return convert_polygon_vector(result); } const char *Py_affine_transform__doc__ = "affine_transform(points, trans)"; static PyObject *Py_affine_transform(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *vertices_obj; agg::trans_affine trans; if (!PyArg_ParseTuple(args, "OO&:affine_transform", &vertices_obj, &convert_trans_affine, &trans)) { return NULL; } try { numpy::array_view<double, 2> vertices(vertices_obj); npy_intp dims[] = { (npy_intp)vertices.size(), 2 }; numpy::array_view<double, 2> result(dims); CALL_CPP("affine_transform", (affine_transform_2d(vertices, trans, result))); return result.pyobj(); } catch (py::exception &) { PyErr_Clear(); try { numpy::array_view<double, 1> vertices(vertices_obj); npy_intp dims[] = { (npy_intp)vertices.size() }; numpy::array_view<double, 1> result(dims); CALL_CPP("affine_transform", (affine_transform_1d(vertices, trans, result))); return result.pyobj(); } catch (py::exception &) { return NULL; } } } const char *Py_count_bboxes_overlapping_bbox__doc__ = "count_bboxes_overlapping_bbox(bbox, bboxes)"; static PyObject *Py_count_bboxes_overlapping_bbox(PyObject *self, PyObject *args, PyObject *kwds) { agg::rect_d bbox; numpy::array_view<const double, 3> bboxes; int result; if (!PyArg_ParseTuple(args, "O&O&:count_bboxes_overlapping_bbox", &convert_rect, &bbox, &convert_bboxes, &bboxes)) { return NULL; } CALL_CPP("count_bboxes_overlapping_bbox", (result = count_bboxes_overlapping_bbox(bbox, bboxes))); return PyLong_FromLong(result); } const char *Py_path_intersects_path__doc__ = "path_intersects_path(path1, path2, filled=False)"; static PyObject *Py_path_intersects_path(PyObject *self, PyObject *args, PyObject *kwds) { py::PathIterator p1; py::PathIterator p2; agg::trans_affine t1; agg::trans_affine t2; int filled = 0; const char *names[] = { "p1", "p2", "filled", NULL }; bool result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&i:path_intersects_path", (char **)names, &convert_path, &p1, &convert_path, &p2, &filled)) { return NULL; } CALL_CPP("path_intersects_path", (result = path_intersects_path(p1, p2))); if (filled) { if (!result) { CALL_CPP("path_intersects_path", (result = path_in_path(p1, t1, p2, t2))); } if (!result) { CALL_CPP("path_intersects_path", (result = path_in_path(p2, t1, p1, t2))); } } if (result) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } const char *Py_path_intersects_rectangle__doc__ = "path_intersects_rectangle(path, rect_x1, rect_y1, rect_x2, rect_y2, filled=False)"; static PyObject *Py_path_intersects_rectangle(PyObject *self, PyObject *args, PyObject *kwds) { py::PathIterator path; double rect_x1, rect_y1, rect_x2, rect_y2; int filled = 0; const char *names[] = { "path", "rect_x1", "rect_y1", "rect_x2", "rect_y2", "filled", NULL }; bool result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&dddd|i:path_intersects_rectangle", (char **)names, &convert_path, &path, &rect_x1, &rect_y1, &rect_x2, &rect_y2, &filled)) { return NULL; } CALL_CPP("path_intersects_rectangle", (result = path_intersects_rectangle(path, rect_x1, rect_y1, rect_x2, rect_y2, filled))); if (result) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } const char *Py_convert_path_to_polygons__doc__ = "convert_path_to_polygons(path, trans, width=0, height=0)"; static PyObject *Py_convert_path_to_polygons(PyObject *self, PyObject *args, PyObject *kwds) { py::PathIterator path; agg::trans_affine trans; double width = 0.0, height = 0.0; int closed_only = 1; std::vector<Polygon> result; const char *names[] = { "path", "transform", "width", "height", "closed_only", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&|ddi:convert_path_to_polygons", (char **)names, &convert_path, &path, &convert_trans_affine, &trans, &width, &height, &closed_only)) { return NULL; } CALL_CPP("convert_path_to_polygons", (convert_path_to_polygons(path, trans, width, height, closed_only, result))); return convert_polygon_vector(result); } const char *Py_cleanup_path__doc__ = "cleanup_path(path, trans, remove_nans, clip_rect, snap_mode, stroke_width, simplify, " "return_curves, sketch)"; static PyObject *Py_cleanup_path(PyObject *self, PyObject *args, PyObject *kwds) { py::PathIterator path; agg::trans_affine trans; int remove_nans; agg::rect_d clip_rect; e_snap_mode snap_mode; double stroke_width; PyObject *simplifyobj; bool simplify = false; int return_curves; SketchParams sketch; if (!PyArg_ParseTuple(args, "O&O&iO&O&dOiO&:cleanup_path", &convert_path, &path, &convert_trans_affine, &trans, &remove_nans, &convert_rect, &clip_rect, &convert_snap, &snap_mode, &stroke_width, &simplifyobj, &return_curves, &convert_sketch_params, &sketch)) { return NULL; } if (simplifyobj == Py_None) { simplify = path.should_simplify(); } else if (PyObject_IsTrue(simplifyobj)) { simplify = true; } bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2); std::vector<double> vertices; std::vector<npy_uint8> codes; CALL_CPP("cleanup_path", (cleanup_path(path, trans, remove_nans, do_clip, clip_rect, snap_mode, stroke_width, simplify, return_curves, sketch, vertices, codes))); size_t length = codes.size(); npy_intp vertices_dims[] = {(npy_intp)length, 2 }; numpy::array_view<double, 2> pyvertices(vertices_dims); npy_intp codes_dims[] = {(npy_intp)length }; numpy::array_view<unsigned char, 1> pycodes(codes_dims); memcpy(pyvertices.data(), &vertices[0], sizeof(double) * 2 * length); memcpy(pycodes.data(), &codes[0], sizeof(unsigned char) * length); return Py_BuildValue("NN", pyvertices.pyobj(), pycodes.pyobj()); } const char *Py_convert_to_string__doc__ = "convert_to_string(path, trans, " "clip_rect, simplify, sketch, precision, codes, postfix)"; static PyObject *Py_convert_to_string(PyObject *self, PyObject *args, PyObject *kwds) { py::PathIterator path; agg::trans_affine trans; agg::rect_d cliprect; PyObject *simplifyobj; bool simplify = false; SketchParams sketch; int precision; PyObject *codesobj; char *codes[5]; int postfix; char *buffer = NULL; size_t buffersize; PyObject *result; int status; if (!PyArg_ParseTuple(args, "O&O&O&OO&iOi:convert_to_string", &convert_path, &path, &convert_trans_affine, &trans, &convert_rect, &cliprect, &simplifyobj, &convert_sketch_params, &sketch, &precision, &codesobj, &postfix)) { return NULL; } if (simplifyobj == Py_None) { simplify = path.should_simplify(); } else if (PyObject_IsTrue(simplifyobj)) { simplify = true; } if (!PySequence_Check(codesobj)) { return NULL; } if (PySequence_Size(codesobj) != 5) { PyErr_SetString( PyExc_ValueError, "codes must be a 5-length sequence of byte strings"); return NULL; } for (int i = 0; i < 5; ++i) { PyObject *item = PySequence_GetItem(codesobj, i); if (item == NULL) { return NULL; } codes[i] = PyBytes_AsString(item); if (codes[i] == NULL) { return NULL; } } CALL_CPP("convert_to_string", (status = convert_to_string( path, trans, cliprect, simplify, sketch, precision, codes, (bool)postfix, &buffer, &buffersize))); if (status) { free(buffer); if (status == 1) { PyErr_SetString(PyExc_MemoryError, "Memory error"); } else if (status == 2) { PyErr_SetString(PyExc_ValueError, "Malformed path codes"); } return NULL; } if (buffersize == 0) { result = PyBytes_FromString(""); } else { result = PyBytes_FromStringAndSize(buffer, buffersize); } free(buffer); return result; } const char *Py_is_sorted__doc__ = "is_sorted(array)\n\n" "Returns True if 1-D array is monotonically increasing, ignoring NaNs\n"; static PyObject *Py_is_sorted(PyObject *self, PyObject *obj) { npy_intp size; bool result; PyArrayObject *array = (PyArrayObject *)PyArray_FromAny( obj, NULL, 1, 1, 0, NULL); if (array == NULL) { return NULL; } size = PyArray_DIM(array, 0); if (size < 2) { Py_DECREF(array); Py_RETURN_TRUE; } /* Handle just the most common types here, otherwise coerce to double */ switch(PyArray_TYPE(array)) { case NPY_INT: { _is_sorted_int<npy_int> is_sorted; result = is_sorted(array); } break; case NPY_LONG: { _is_sorted_int<npy_long> is_sorted; result = is_sorted(array); } break; case NPY_LONGLONG: { _is_sorted_int<npy_longlong> is_sorted; result = is_sorted(array); } break; case NPY_FLOAT: { _is_sorted<npy_float> is_sorted; result = is_sorted(array); } break; case NPY_DOUBLE: { _is_sorted<npy_double> is_sorted; result = is_sorted(array); } break; default: { Py_DECREF(array); array = (PyArrayObject *)PyArray_FromObject(obj, NPY_DOUBLE, 1, 1); if (array == NULL) { return NULL; } _is_sorted<npy_double> is_sorted; result = is_sorted(array); } } Py_DECREF(array); if (result) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } extern "C" { static PyMethodDef module_functions[] = { {"point_in_path", (PyCFunction)Py_point_in_path, METH_VARARGS, Py_point_in_path__doc__}, {"points_in_path", (PyCFunction)Py_points_in_path, METH_VARARGS, Py_points_in_path__doc__}, {"point_on_path", (PyCFunction)Py_point_on_path, METH_VARARGS, Py_point_on_path__doc__}, {"points_on_path", (PyCFunction)Py_points_on_path, METH_VARARGS, Py_points_on_path__doc__}, {"get_path_extents", (PyCFunction)Py_get_path_extents, METH_VARARGS, Py_get_path_extents__doc__}, {"update_path_extents", (PyCFunction)Py_update_path_extents, METH_VARARGS, Py_update_path_extents__doc__}, {"get_path_collection_extents", (PyCFunction)Py_get_path_collection_extents, METH_VARARGS, Py_get_path_collection_extents__doc__}, {"point_in_path_collection", (PyCFunction)Py_point_in_path_collection, METH_VARARGS, Py_point_in_path_collection__doc__}, {"path_in_path", (PyCFunction)Py_path_in_path, METH_VARARGS, Py_path_in_path__doc__}, {"clip_path_to_rect", (PyCFunction)Py_clip_path_to_rect, METH_VARARGS, Py_clip_path_to_rect__doc__}, {"affine_transform", (PyCFunction)Py_affine_transform, METH_VARARGS, Py_affine_transform__doc__}, {"count_bboxes_overlapping_bbox", (PyCFunction)Py_count_bboxes_overlapping_bbox, METH_VARARGS, Py_count_bboxes_overlapping_bbox__doc__}, {"path_intersects_path", (PyCFunction)Py_path_intersects_path, METH_VARARGS|METH_KEYWORDS, Py_path_intersects_path__doc__}, {"path_intersects_rectangle", (PyCFunction)Py_path_intersects_rectangle, METH_VARARGS|METH_KEYWORDS, Py_path_intersects_rectangle__doc__}, {"convert_path_to_polygons", (PyCFunction)Py_convert_path_to_polygons, METH_VARARGS|METH_KEYWORDS, Py_convert_path_to_polygons__doc__}, {"cleanup_path", (PyCFunction)Py_cleanup_path, METH_VARARGS, Py_cleanup_path__doc__}, {"convert_to_string", (PyCFunction)Py_convert_to_string, METH_VARARGS, Py_convert_to_string__doc__}, {"is_sorted", (PyCFunction)Py_is_sorted, METH_O, Py_is_sorted__doc__}, {NULL} }; #if PY3K static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_path", NULL, 0, module_functions, NULL, NULL, NULL, NULL }; #define INITERROR return NULL PyMODINIT_FUNC PyInit__path(void) #else #define INITERROR return PyMODINIT_FUNC init_path(void) #endif { PyObject *m; #if PY3K m = PyModule_Create(&moduledef); #else m = Py_InitModule3("_path", module_functions, NULL); #endif if (m == NULL) { INITERROR; } import_array(); #if PY3K return m; #endif } }