Add mathutils.geometry.delaunay_2d_cdt() function to Python API.
Provides Python API access to recently added Constrained Delaunay Triangulation routine. Reviewed in D5467.
This commit is contained in:
		| @@ -25,6 +25,7 @@ | ||||
| #include "BLI_math.h" | ||||
| #include "BLI_utildefines.h" | ||||
|  | ||||
| #include "../generic/py_capi_utils.h" | ||||
| #include "../generic/python_utildefines.h" | ||||
|  | ||||
| #ifndef MATH_STANDALONE | ||||
| @@ -328,6 +329,153 @@ int mathutils_array_parse_alloc_v(float **array, | ||||
|   return size; | ||||
| } | ||||
|  | ||||
| /* Parse an sequence array_dim integers into array. */ | ||||
| int mathutils_int_array_parse(int *array, int array_dim, PyObject *value, const char *error_prefix) | ||||
| { | ||||
|   int size, i; | ||||
|   PyObject *value_fast, **value_fast_items, *item; | ||||
|  | ||||
|   if (!(value_fast = PySequence_Fast(value, error_prefix))) { | ||||
|     /* PySequence_Fast sets the error */ | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   if ((size = PySequence_Fast_GET_SIZE(value_fast)) != array_dim) { | ||||
|     PyErr_Format(PyExc_ValueError, | ||||
|                  "%.200s: sequence size is %d, expected %d", | ||||
|                  error_prefix, | ||||
|                  size, | ||||
|                  array_dim); | ||||
|     Py_DECREF(value_fast); | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   value_fast_items = PySequence_Fast_ITEMS(value_fast); | ||||
|   i = size; | ||||
|   while (i > 0) { | ||||
|     i--; | ||||
|     if (((array[i] = PyC_Long_AsI32((item = value_fast_items[i]))) == -1) && PyErr_Occurred()) { | ||||
|       PyErr_Format(PyExc_TypeError, "%.200s: sequence index %d expected an int", error_prefix, i); | ||||
|       size = -1; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   Py_DECREF(value_fast); | ||||
|  | ||||
|   return size; | ||||
| } | ||||
|  | ||||
| /* Parse sequence of array_dim sequences of integers and return allocated result. */ | ||||
| int mathutils_array_parse_alloc_vi(int **array, | ||||
|                                    int array_dim, | ||||
|                                    PyObject *value, | ||||
|                                    const char *error_prefix) | ||||
| { | ||||
|   PyObject *value_fast; | ||||
|   int i, size; | ||||
|  | ||||
|   if (!(value_fast = PySequence_Fast(value, error_prefix))) { | ||||
|     /* PySequence_Fast sets the error */ | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   size = PySequence_Fast_GET_SIZE(value_fast); | ||||
|  | ||||
|   if (size != 0) { | ||||
|     PyObject **value_fast_items = PySequence_Fast_ITEMS(value_fast); | ||||
|     int *ip; | ||||
|  | ||||
|     ip = *array = PyMem_Malloc(size * array_dim * sizeof(int)); | ||||
|  | ||||
|     for (i = 0; i < size; i++, ip += array_dim) { | ||||
|       PyObject *item = value_fast_items[i]; | ||||
|  | ||||
|       if (mathutils_int_array_parse(ip, array_dim, item, error_prefix) == -1) { | ||||
|         PyMem_Free(*array); | ||||
|         *array = NULL; | ||||
|         size = -1; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Py_DECREF(value_fast); | ||||
|   return size; | ||||
| } | ||||
|  | ||||
| /* Parse sequence of variable-length sequences of int and return allocated | ||||
|  * triple of arrays to represent the result: | ||||
|  * The flattened sequences are put into *array. | ||||
|  * The start index of each sequence goes into start_table. | ||||
|  * The length of each index goes into len_table. | ||||
|  */ | ||||
| int mathutils_array_parse_alloc_viseq( | ||||
|     int **array, int **start_table, int **len_table, PyObject *value, const char *error_prefix) | ||||
| { | ||||
|   PyObject *value_fast, *subseq; | ||||
|   int i, size, start, subseq_len; | ||||
|   int *ip; | ||||
|  | ||||
|   *array = NULL; | ||||
|   *start_table = NULL; | ||||
|   *len_table = NULL; | ||||
|   if (!(value_fast = PySequence_Fast(value, error_prefix))) { | ||||
|     /* PySequence_Fast sets the error */ | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   size = PySequence_Fast_GET_SIZE(value_fast); | ||||
|  | ||||
|   if (size != 0) { | ||||
|     PyObject **value_fast_items = PySequence_Fast_ITEMS(value_fast); | ||||
|  | ||||
|     *start_table = PyMem_Malloc(size * sizeof(int)); | ||||
|     *len_table = PyMem_Malloc(size * sizeof(int)); | ||||
|  | ||||
|     /* First pass to set starts and len, and calculate size of array needed */ | ||||
|     start = 0; | ||||
|     for (i = 0; i < size; i++) { | ||||
|       subseq = value_fast_items[i]; | ||||
|       if ((subseq_len = (int)PySequence_Size(subseq)) == -1) { | ||||
|         PyErr_Format( | ||||
|             PyExc_ValueError, "%.200s: sequence expected to have subsequences", error_prefix); | ||||
|         PyMem_Free(*start_table); | ||||
|         PyMem_Free(*len_table); | ||||
|         Py_DECREF(value_fast); | ||||
|         *start_table = NULL; | ||||
|         *len_table = NULL; | ||||
|         return -1; | ||||
|       } | ||||
|       (*start_table)[i] = start; | ||||
|       (*len_table)[i] = subseq_len; | ||||
|       start += subseq_len; | ||||
|     } | ||||
|  | ||||
|     ip = *array = PyMem_Malloc(start * sizeof(int)); | ||||
|  | ||||
|     /* Second pass to parse the subsequences into array */ | ||||
|     for (i = 0; i < size; i++) { | ||||
|       subseq = value_fast_items[i]; | ||||
|       subseq_len = (*len_table)[i]; | ||||
|  | ||||
|       if (mathutils_int_array_parse(ip, subseq_len, subseq, error_prefix) == -1) { | ||||
|         PyMem_Free(*array); | ||||
|         PyMem_Free(*start_table); | ||||
|         PyMem_Free(*len_table); | ||||
|         *array = NULL; | ||||
|         *len_table = NULL; | ||||
|         *start_table = NULL; | ||||
|         size = -1; | ||||
|         break; | ||||
|       } | ||||
|       ip += subseq_len; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Py_DECREF(value_fast); | ||||
|   return size; | ||||
| } | ||||
|  | ||||
| int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix) | ||||
| { | ||||
|   if (EulerObject_Check(value)) { | ||||
|   | ||||
| @@ -167,6 +167,16 @@ int mathutils_array_parse_alloc_v(float **array, | ||||
|                                   int array_dim, | ||||
|                                   PyObject *value, | ||||
|                                   const char *error_prefix); | ||||
| int mathutils_int_array_parse(int *array, | ||||
|                               int array_dim, | ||||
|                               PyObject *value, | ||||
|                               const char *error_prefix); | ||||
| int mathutils_array_parse_alloc_vi(int **array, | ||||
|                                    int array_dim, | ||||
|                                    PyObject *value, | ||||
|                                    const char *error_prefix); | ||||
| int mathutils_array_parse_alloc_viseq( | ||||
|     int **array, int **start_table, int **len_table, PyObject *value, const char *error_prefix); | ||||
| int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix); | ||||
|  | ||||
| Py_hash_t mathutils_array_hash(const float *float_array, size_t array_len); | ||||
|   | ||||
| @@ -29,6 +29,7 @@ | ||||
| #  include "BLI_blenlib.h" | ||||
| #  include "BLI_boxpack_2d.h" | ||||
| #  include "BLI_convexhull_2d.h" | ||||
| #  include "BLI_delaunay_2d.h" | ||||
| #  include "BKE_displist.h" | ||||
| #  include "BKE_curve.h" | ||||
| #endif | ||||
| @@ -1507,6 +1508,186 @@ static PyObject *M_Geometry_convex_hull_2d(PyObject *UNUSED(self), PyObject *poi | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| /* Return a PyObject that is a list of lists, using the flattened list array | ||||
|  * to fill values, with start_table and len_table giving the start index | ||||
|  * and length of the toplevel_len sublists | ||||
|  */ | ||||
| static PyObject *list_of_lists_from_arrays(int *array, | ||||
|                                            int *start_table, | ||||
|                                            int *len_table, | ||||
|                                            int toplevel_len) | ||||
| { | ||||
|   PyObject *ret, *sublist; | ||||
|   int i, j, sublist_len, sublist_start, val; | ||||
|  | ||||
|   ret = PyList_New(toplevel_len); | ||||
|   for (i = 0; i < toplevel_len; i++) { | ||||
|     sublist_len = len_table[i]; | ||||
|     sublist = PyList_New(sublist_len); | ||||
|     sublist_start = start_table[i]; | ||||
|     for (j = 0; j < sublist_len; j++) { | ||||
|       val = array[sublist_start + j]; | ||||
|       PyList_SET_ITEM(sublist, j, PyLong_FromLong(val)); | ||||
|     } | ||||
|     PyList_SET_ITEM(ret, i, sublist); | ||||
|   } | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| PyDoc_STRVAR( | ||||
|     M_Geometry_delaunay_2d_cdt_doc, | ||||
|     ".. function:: delaunay_2d_cdt(vert_coords, edges, faces, output_type, epsilon)\n" | ||||
|     "\n" | ||||
|     "Computes the Constrained Delaunay Triangulation of a set of vertices, " | ||||
|     "with edges and faces that must appear in the triangulation. " | ||||
|     "Some triangles may be eaten away, or combined with other triangles, " | ||||
|     "according to output type. " | ||||
|     "The returned verts may be in a different order from input verts, may be moved " | ||||
|     "slightly, and may be merged with other nearby verts. " | ||||
|     "The three returned orig lists give, for each of verts, edges, and faces, the list of " | ||||
|     "input element indices corresponding to the positionally same output element. " | ||||
|     "For edges, the orig indices start with the input edges and then continue " | ||||
|     "with the edges implied by each of the faces (n of them for an n-gon).\n" | ||||
|     "\n" | ||||
|     "   :arg vert_coords: Vertex coordinates (2d)\n" | ||||
|     "   :type vert_coords: list of :class:`mathutils.Vector`\n" | ||||
|     "   :arg edges: Edges, as pairs of indices in `vert_coords`\n" | ||||
|     "   :type edges: list of (int, int)\n" | ||||
|     "   :arg faces: Faces, each sublist is a face, as indices in `vert_coords` (CCW oriented)\n" | ||||
|     "   :type faces: list of list of int\n" | ||||
|     "   :arg output_type: What output looks like. 0 => triangles with convex hull. " | ||||
|     "1 => triangles inside constraints. " | ||||
|     "2 => the input constraints, intersected. " | ||||
|     "3 => like 2 but with extra edges to make valid BMesh faces.\n" | ||||
|     "   :type output_type: int\\n" | ||||
|     "   :arg epsilon: For nearness tests; should not be zero\n" | ||||
|     "   :type epsilon: float\n" | ||||
|     "   :return: Output tuple, (vert_coords, edges, faces, orig_verts, orig_edges, orig_faces)\n" | ||||
|     "   :rtype: (list of `mathutils.Vector`, " | ||||
|     "list of (int, int), " | ||||
|     "list of list of int, " | ||||
|     "list of list of int, " | ||||
|     "list of list of int, " | ||||
|     "list of list of int)\n" | ||||
|     "\n"); | ||||
| static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *args) | ||||
| { | ||||
|   const char *error_prefix = "delaunay_2d_cdt"; | ||||
|   PyObject *vert_coords, *edges, *faces, *item; | ||||
|   int output_type; | ||||
|   float epsilon; | ||||
|   float(*in_coords)[2] = NULL; | ||||
|   int(*in_edges)[2] = NULL; | ||||
|   int *in_faces = NULL; | ||||
|   int *in_faces_start_table = NULL; | ||||
|   int *in_faces_len_table = NULL; | ||||
|   Py_ssize_t vert_coords_len, edges_len, faces_len; | ||||
|   CDT_input in; | ||||
|   CDT_result *res = NULL; | ||||
|   PyObject *out_vert_coords = NULL; | ||||
|   PyObject *out_edges = NULL; | ||||
|   PyObject *out_faces = NULL; | ||||
|   PyObject *out_orig_verts = NULL; | ||||
|   PyObject *out_orig_edges = NULL; | ||||
|   PyObject *out_orig_faces = NULL; | ||||
|   PyObject *ret_value = NULL; | ||||
|   int i; | ||||
|  | ||||
|   if (!PyArg_ParseTuple( | ||||
|           args, "OOOif:delaunay_2d_cdt", &vert_coords, &edges, &faces, &output_type, &epsilon)) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   vert_coords_len = mathutils_array_parse_alloc_v( | ||||
|       (float **)&in_coords, 2, vert_coords, error_prefix); | ||||
|   if (vert_coords_len == -1) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   edges_len = mathutils_array_parse_alloc_vi((int **)&in_edges, 2, edges, error_prefix); | ||||
|   if (edges_len == -1) { | ||||
|     goto exit_cdt; | ||||
|   } | ||||
|  | ||||
|   faces_len = mathutils_array_parse_alloc_viseq( | ||||
|       &in_faces, &in_faces_start_table, &in_faces_len_table, faces, error_prefix); | ||||
|   if (faces_len == -1) { | ||||
|     goto exit_cdt; | ||||
|   } | ||||
|  | ||||
|   in.verts_len = (int)vert_coords_len; | ||||
|   in.vert_coords = in_coords; | ||||
|   in.edges_len = edges_len; | ||||
|   in.faces_len = faces_len; | ||||
|   in.edges = in_edges; | ||||
|   in.faces = in_faces; | ||||
|   in.faces_start_table = in_faces_start_table; | ||||
|   in.faces_len_table = in_faces_len_table; | ||||
|   in.epsilon = epsilon; | ||||
|  | ||||
|   res = BLI_delaunay_2d_cdt_calc(&in, output_type); | ||||
|  | ||||
|   ret_value = PyTuple_New(6); | ||||
|  | ||||
|   out_vert_coords = PyList_New(res->verts_len); | ||||
|   for (i = 0; i < res->verts_len; i++) { | ||||
|     item = Vector_CreatePyObject(res->vert_coords[i], 2, NULL); | ||||
|     if (item == NULL) { | ||||
|       Py_DECREF(ret_value); | ||||
|       Py_DECREF(out_vert_coords); | ||||
|       goto exit_cdt; | ||||
|     } | ||||
|     PyList_SET_ITEM(out_vert_coords, i, item); | ||||
|   } | ||||
|   PyTuple_SET_ITEM(ret_value, 0, out_vert_coords); | ||||
|  | ||||
|   out_edges = PyList_New(res->edges_len); | ||||
|   for (i = 0; i < res->edges_len; i++) { | ||||
|     item = PyTuple_New(2); | ||||
|     PyTuple_SET_ITEM(item, 0, PyLong_FromLong((long)res->edges[i][0])); | ||||
|     PyTuple_SET_ITEM(item, 1, PyLong_FromLong((long)res->edges[i][1])); | ||||
|     PyList_SET_ITEM(out_edges, i, item); | ||||
|   } | ||||
|   PyTuple_SET_ITEM(ret_value, 1, out_edges); | ||||
|  | ||||
|   out_faces = list_of_lists_from_arrays( | ||||
|       res->faces, res->faces_start_table, res->faces_len_table, res->faces_len); | ||||
|   PyTuple_SET_ITEM(ret_value, 2, out_faces); | ||||
|  | ||||
|   out_orig_verts = list_of_lists_from_arrays( | ||||
|       res->verts_orig, res->verts_orig_start_table, res->verts_orig_len_table, res->verts_len); | ||||
|   PyTuple_SET_ITEM(ret_value, 3, out_orig_verts); | ||||
|  | ||||
|   out_orig_edges = list_of_lists_from_arrays( | ||||
|       res->edges_orig, res->edges_orig_start_table, res->edges_orig_len_table, res->edges_len); | ||||
|   PyTuple_SET_ITEM(ret_value, 4, out_orig_edges); | ||||
|  | ||||
|   out_orig_faces = list_of_lists_from_arrays( | ||||
|       res->faces_orig, res->faces_orig_start_table, res->faces_orig_len_table, res->faces_len); | ||||
|   PyTuple_SET_ITEM(ret_value, 5, out_orig_faces); | ||||
|  | ||||
| exit_cdt: | ||||
|   if (in_coords != NULL) { | ||||
|     PyMem_Free(in_coords); | ||||
|   } | ||||
|   if (in_edges != NULL) { | ||||
|     PyMem_Free(in_edges); | ||||
|   } | ||||
|   if (in_faces != NULL) { | ||||
|     PyMem_Free(in_faces); | ||||
|   } | ||||
|   if (in_faces_start_table != NULL) { | ||||
|     PyMem_Free(in_faces_start_table); | ||||
|   } | ||||
|   if (in_faces_len_table != NULL) { | ||||
|     PyMem_Free(in_faces_len_table); | ||||
|   } | ||||
|   if (res) { | ||||
|     BLI_delaunay_2d_cdt_free(res); | ||||
|   } | ||||
|   return ret_value; | ||||
| } | ||||
|  | ||||
| #endif /* MATH_STANDALONE */ | ||||
|  | ||||
| static PyMethodDef M_Geometry_methods[] = { | ||||
| @@ -1593,6 +1774,10 @@ static PyMethodDef M_Geometry_methods[] = { | ||||
|      (PyCFunction)M_Geometry_convex_hull_2d, | ||||
|      METH_O, | ||||
|      M_Geometry_convex_hull_2d_doc}, | ||||
|     {"delaunay_2d_cdt", | ||||
|      (PyCFunction)M_Geometry_delaunay_2d_cdt, | ||||
|      METH_VARARGS, | ||||
|      M_Geometry_delaunay_2d_cdt_doc}, | ||||
|     {"box_fit_2d", (PyCFunction)M_Geometry_box_fit_2d, METH_O, M_Geometry_box_fit_2d_doc}, | ||||
|     {"box_pack_2d", (PyCFunction)M_Geometry_box_pack_2d, METH_O, M_Geometry_box_pack_2d_doc}, | ||||
| #endif | ||||
|   | ||||
		Reference in New Issue
	
	Block a user