From 1191f54ef46648f7bae207a3d573682c5cee2fee Mon Sep 17 00:00:00 2001 From: Garux Date: Mon, 22 Oct 2018 18:33:40 +0300 Subject: [PATCH] add https://github.com/leomccormack/convhull_3d/blob/master/convhull_3d.h --- libs/convhull_3d.h | 996 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 996 insertions(+) create mode 100644 libs/convhull_3d.h diff --git a/libs/convhull_3d.h b/libs/convhull_3d.h new file mode 100644 index 00000000..18be68fe --- /dev/null +++ b/libs/convhull_3d.h @@ -0,0 +1,996 @@ +/* + Copyright (c) 2017-2018 Leo McCormack + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +/* + * Filename: + * convhull_3d.h + * Description: + * A header only C implementation of the 3-D quickhull algorithm. + * The code is largely derived from the "computational-geometry-toolbox" + * by George Papazafeiropoulos (c) 2014, originally distributed under + * the BSD (2-clause) license. + * To include this implementation in a project, simply add this: + * #define CONVHULL_3D_ENABLE + * #include "convhull_3d.h" + * By default, the algorithm uses double floating point precision. To + * use single precision (less accurate but quicker), also add this: + * #define CONVHULL_3D_USE_FLOAT_PRECISION + * If your project has CBLAS linked, then you can also speed things up + * a tad by adding this: + * #define CONVHULL_3D_USE_CBLAS + * The code is C++ compiler safe. + * Reference: "The Quickhull Algorithm for Convex Hull, C. Bradford + * Barber, David P. Dobkin and Hannu Huhdanpaa, Geometry + * Center Technical Report GCG53, July 30, 1993" + * Dependencies: + * cblas (optional for speed ups, especially for very large meshes) + * Author, date created: + * Leo McCormack, 02.10.2017 + */ + +/********** + * PUBLIC: + *********/ + +#ifndef CONVHULL_3D_INCLUDED +#define CONVHULL_3D_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef CONVHULL_3D_USE_FLOAT_PRECISION +typedef float CH_FLOAT; +#else +typedef double CH_FLOAT; +#endif +typedef struct _ch_vertex { + union { + CH_FLOAT v[3]; + struct{ + CH_FLOAT x, y, z; + }; + }; +} ch_vertex; +typedef ch_vertex ch_vec3; + +/* builds the convexhull, returning the face indices corresponding to "in_vertices" */ +void convhull_3d_build(/* input arguments */ + ch_vertex* const in_vertices, /* vector of input vertices; nVert x 1 */ + const int nVert, /* number of vertices */ + /* output arguments */ + int** out_faces, /* & of empty int*, output face indices; flat: nOut_faces x 3 */ + int* nOut_faces); /* & of int, number of output face indices */ + +/* exports the vertices, face indices, and face normals, as an 'obj' file, ready for GPU */ +void convhull_3d_export_obj(/* input arguments */ + ch_vertex* const vertices, /* vector of input vertices; nVert x 1 */ + const int nVert, /* number of vertices */ + int* const faces, /* face indices; flat: nFaces x 3 */ + const int nFaces, /* number of faces in hull */ + const int keepOnlyUsedVerticesFLAG, /* 0: exports in_vertices, 1: exports only used vertices */ + char* const obj_filename); /* obj filename, WITHOUT extension */ + +/* exports the vertices, face indices, and face normals, as an 'm' file, for MatLab verification */ +void convhull_3d_export_m(/* input arguments */ + ch_vertex* const vertices, /* vector of input vertices; nVert x 1 */ + const int nVert, /* number of vertices */ + int* const faces, /* face indices; flat: nFaces x 3 */ + const int nFaces, /* number of faces in hull */ + char* const m_filename); /* m filename, WITHOUT extension */ + +/* reads an 'obj' file and extracts only the vertices */ +void extractVerticesFromObjFile(/* input arguments */ + char* const obj_filename, /* obj filename, WITHOUT extension */ + /* output arguments */ + ch_vertex** out_vertices, /* & of empty ch_vertex*, output vertices; out_nVert x 1 */ + int* out_nVert); /* & of int, number of vertices */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /* CONVHULL_3D_INCLUDED */ + + +/************ + * INTERNAL: + ***********/ + +#ifdef CONVHULL_3D_ENABLE + +#include +#include +#include +#include +#include +#include +#include +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) + #define CV_STRNCPY(a,b,c) strncpy_s(a,c+1,b,c); + #define CV_STRCAT(a,b) strcat_s(a,sizeof(b),b); +#else + #define CV_STRNCPY(a,b,c) strncpy(a,b,c); + #define CV_STRCAT(a,b) strcat(a,b); +#endif +#ifdef CONVHULL_3D_USE_FLOAT_PRECISION + #define CH_FLT_MIN FLT_MIN + #define CH_FLT_MAX FLT_MAX + #define CH_NOISE_VAL 0.00001f +#else + #define CH_FLT_MIN DBL_MIN + #define CH_FLT_MAX DBL_MAX + #define CH_NOISE_VAL 0.0000001 +#endif +#ifndef MIN + #define MIN(a,b) (( (a) < (b) ) ? (a) : (b) ) +#endif +#ifndef MAX + #define MAX(a,b) (( (a) > (b) ) ? (a) : (b) ) +#endif +#define CH_MAX_NUM_FACES 50000 + +/* structs for qsort */ +typedef struct float_w_idx { + CH_FLOAT val; + int idx; +}float_w_idx; + +typedef struct int_w_idx { + int val; + int idx; +}int_w_idx; + +/* internal functions prototypes: */ +static int cmp_asc_float(const void*, const void*); +static int cmp_desc_float(const void*, const void*); +static int cmp_asc_int(const void*, const void*); +static int cmp_desc_int(const void*, const void*); +static void sort_float(CH_FLOAT*, CH_FLOAT*, int*, int, int); +static void sort_int(int*, int*, int*, int, int); +static ch_vec3 cross(ch_vec3*, ch_vec3*); +static CH_FLOAT det_4x4(CH_FLOAT*); +static void plane_3d(CH_FLOAT*, CH_FLOAT*, CH_FLOAT*); +static void ismember(int*, int*, int*, int, int); + +/* internal functions definitions: */ +static int cmp_asc_float(const void *a,const void *b) { + struct float_w_idx *a1 = (struct float_w_idx*)a; + struct float_w_idx *a2 = (struct float_w_idx*)b; + if((*a1).val<(*a2).val)return -1; + else if((*a1).val>(*a2).val)return 1; + else return 0; +} + +static int cmp_desc_float(const void *a,const void *b) { + struct float_w_idx *a1 = (struct float_w_idx*)a; + struct float_w_idx *a2 = (struct float_w_idx*)b; + if((*a1).val>(*a2).val)return -1; + else if((*a1).val<(*a2).val)return 1; + else return 0; +} + +static int cmp_asc_int(const void *a,const void *b) { + struct int_w_idx *a1 = (struct int_w_idx*)a; + struct int_w_idx *a2 = (struct int_w_idx*)b; + if((*a1).val<(*a2).val)return -1; + else if((*a1).val>(*a2).val)return 1; + else return 0; +} + +static int cmp_desc_int(const void *a,const void *b) { + struct int_w_idx *a1 = (struct int_w_idx*)a; + struct int_w_idx *a2 = (struct int_w_idx*)b; + if((*a1).val>(*a2).val)return -1; + else if((*a1).val<(*a2).val)return 1; + else return 0; +} + +static void sort_float +( + CH_FLOAT* in_vec, /* vector[len] to be sorted */ + CH_FLOAT* out_vec, /* if NULL, then in_vec is sorted "in-place" */ + int* new_idices, /* set to NULL if you don't need them */ + int len, /* number of elements in vectors, must be consistent with the input data */ + int descendFLAG /* !1:ascending, 1:descending */ +) +{ + int i; + struct float_w_idx *data; + + data = (float_w_idx*)malloc(len*sizeof(float_w_idx)); + for(i=0;iy * v2->z - v1->z * v2->y; + cross.y = v1->z * v2->x - v1->x * v2->z; + cross.z = v1->x * v2->y - v1->y * v2->x; + return cross; +} + +/* calculates the determinent of a 4x4 matrix */ +static CH_FLOAT det_4x4(CH_FLOAT* m) { + return + m[3] * m[6] * m[9] * m[12] - m[2] * m[7] * m[9] * m[12] - + m[3] * m[5] * m[10] * m[12] + m[1] * m[7] * m[10] * m[12] + + m[2] * m[5] * m[11] * m[12] - m[1] * m[6] * m[11] * m[12] - + m[3] * m[6] * m[8] * m[13] + m[2] * m[7] * m[8] * m[13] + + m[3] * m[4] * m[10] * m[13] - m[0] * m[7] * m[10] * m[13] - + m[2] * m[4] * m[11] * m[13] + m[0] * m[6] * m[11] * m[13] + + m[3] * m[5] * m[8] * m[14] - m[1] * m[7] * m[8] * m[14] - + m[3] * m[4] * m[9] * m[14] + m[0] * m[7] * m[9] * m[14] + + m[1] * m[4] * m[11] * m[14] - m[0] * m[5] * m[11] * m[14] - + m[2] * m[5] * m[8] * m[15] + m[1] * m[6] * m[8] * m[15] + + m[2] * m[4] * m[9] * m[15] - m[0] * m[6] * m[9] * m[15] - + m[1] * m[4] * m[10] * m[15] + m[0] * m[5] * m[10] * m[15]; +} + +/* Calculates the coefficients of the equation of a PLANE in 3D. + * Original Copyright (c) 2014, George Papazafeiropoulos + * Distributed under the BSD (2-clause) license + */ +static void plane_3d +( + CH_FLOAT* p, + CH_FLOAT* c, + CH_FLOAT* d +) +{ + int i, j, k, l; + int r[3]; + CH_FLOAT sign, det, norm_c; + CH_FLOAT pdiff[2][3], pdiff_s[2][2]; + + for(i=0; i<2; i++) + for(j=0; j<3; j++) + pdiff[i][j] = p[(i+1)*3+j] - p[i*3+j]; + memset(c, 0, 3*sizeof(CH_FLOAT)); + sign = 1.0; + for(i=0; i<3; i++) + r[i] = i; + for(i=0; i<3; i++){ + for(j=0; j<2; j++){ + for(k=0, l=0; k<3; k++){ + if(r[k]!=i){ + pdiff_s[j][l] = pdiff[j][k]; + l++; + } + } + } + det = pdiff_s[0][0]*pdiff_s[1][1] - pdiff_s[1][0]*pdiff_s[0][1]; + c[i] = sign * det; + sign *= -1.0; + } + norm_c = (CH_FLOAT)0.0; + for(i=0; i<3; i++) + norm_c += (pow(c[i], 2.0)); + norm_c = sqrt(norm_c); + for(i=0; i<3; i++) + c[i] /= norm_c; + (*d) = (CH_FLOAT)0.0; + for(i=0; i<3; i++) + (*d) += -p[i] * c[i]; +} + +static void ismember +( + int* pLeft, /* left vector; nLeftElements x 1 */ + int* pRight, /* right vector; nRightElements x 1 */ + int* pOut, /* 0, unless pRight elements are present in pLeft then 1; nLeftElements x 1 */ + int nLeftElements, /* number of elements in pLeft */ + int nRightElements /* number of elements in pRight */ +) +{ + int i, j; + memset(pOut, 0, nLeftElements*sizeof(int)); + for(i=0; i< nLeftElements; i++) + for(j=0; j< nRightElements; j++) + if(pLeft[i] == pRight[j] ) + pOut[i] = 1; +} + +/* A C version of the 3D quickhull matlab implementation from here: + * https://www.mathworks.com/matlabcentral/fileexchange/48509-computational-geometry-toolbox?focused=3851550&tab=example + * (*out_faces) is returned as NULL, if triangulation fails * + * Original Copyright (c) 2014, George Papazafeiropoulos + * Distributed under the BSD (2-clause) license + * Reference: "The Quickhull Algorithm for Convex Hull, C. Bradford Barber, David P. Dobkin + * and Hannu Huhdanpaa, Geometry Center Technical Report GCG53, July 30, 1993" + */ +void convhull_3d_build +( + ch_vertex* const in_vertices, + const int nVert, + int** out_faces, + int* nOut_faces +) +{ + int i, j, k, l, h; + int nFaces, p, d; + int* aVec, *faces; + CH_FLOAT dfi, v, max_p, min_p; + CH_FLOAT* points, *cf, *cfi, *df, *p_s, *span; + + if(nVert<3 || in_vertices==NULL){ + (*out_faces) = NULL; + (*nOut_faces) = 0; + return; + } + + /* 3 dimensions. The code should theoretically work for >=2 dimensions, but "plane_3d" and "det_4x4" are hardcoded for 3, + * so would need to be rewritten */ + d = 3; + span = (CH_FLOAT*)malloc(d*sizeof(CH_FLOAT)); + for(j=0; j0) ){ + /* i is the first point of the points left */ + i = pleft[0]; + + /* Delete the point selected */ + for(j=0; j 0.0){ + num_visible_ind++; /* will sum to 0 if none are visible */ + visible_ind[j] = 1; + } + else + visible_ind[j] = 0; + } + num_nonvisible_faces = nFaces - num_visible_ind; + + /* proceed if there are any visible faces */ + if(num_visible_ind!=0){ + /* Find visible face indices */ + visible = (int*)malloc(num_visible_ind*sizeof(int)); + for(j=0, k=0; j CH_MAX_NUM_FACES){ + FUCKED = 1; + nFaces = 0; + break; + } + } + + /* Orient each new face properly */ + hVec = (int*)malloc( nFaces*sizeof(int)); + hVec_mem_face = (int*)malloc( nFaces*sizeof(int)); + for(j=0; j4){ + /* not a valid file */ + free((*out_vertices)); + (*out_vertices) = NULL; + (*out_nVert) = 0; + return; + } + (*out_vertices)[i].v[vertID] = atof(vert_char); + memset(vert_char, 0, 256 * sizeof(char)); + } + prev_char_isDigit = current_char_isDigit; + } + i++; + } + } +} + + +#endif /* CONVHULL_3D_ENABLE */