use convhull_3d for quicker hull calculation

This commit is contained in:
Garux 2018-10-22 18:36:38 +03:00
parent 1191f54ef4
commit 736f89026f
2 changed files with 107 additions and 301 deletions

View File

@ -81,30 +81,6 @@ void convhull_3d_build(/* input arguments */
int** out_faces, /* & of empty int*, output face indices; flat: nOut_faces x 3 */ int** out_faces, /* & of empty int*, output face indices; flat: nOut_faces x 3 */
int* nOut_faces); /* & of int, number of output face indices */ 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 #ifdef __cplusplus
} /*extern "C"*/ } /*extern "C"*/
#endif #endif
@ -116,22 +92,11 @@ void extractVerticesFromObjFile(/* input arguments */
* INTERNAL: * INTERNAL:
***********/ ***********/
#ifdef CONVHULL_3D_ENABLE
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <float.h> #include <float.h>
#include <ctype.h> #include <ctype.h>
#include <string.h>
#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 #ifdef CONVHULL_3D_USE_FLOAT_PRECISION
#define CH_FLT_MIN FLT_MIN #define CH_FLT_MIN FLT_MIN
#define CH_FLT_MAX FLT_MAX #define CH_FLT_MAX FLT_MAX
@ -167,39 +132,38 @@ static int cmp_asc_int(const void*, const void*);
static int cmp_desc_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_float(CH_FLOAT*, CH_FLOAT*, int*, int, int);
static void sort_int(int*, int*, 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 CH_FLOAT det_4x4(CH_FLOAT*);
static void plane_3d(CH_FLOAT*, CH_FLOAT*, CH_FLOAT*); static void plane_3d(CH_FLOAT*, CH_FLOAT*, CH_FLOAT*);
static void ismember(int*, int*, int*, int, int); static void ismember(int*, int*, int*, int, int);
/* internal functions definitions: */ /* internal functions definitions: */
static int cmp_asc_float(const void *a,const void *b) { static int cmp_asc_float(const void *a,const void *b) {
struct float_w_idx *a1 = (struct float_w_idx*)a; const struct float_w_idx *a1 = (const struct float_w_idx*)a;
struct float_w_idx *a2 = (struct float_w_idx*)b; const struct float_w_idx *a2 = (const struct float_w_idx*)b;
if((*a1).val<(*a2).val)return -1; if((*a1).val<(*a2).val)return -1;
else if((*a1).val>(*a2).val)return 1; else if((*a1).val>(*a2).val)return 1;
else return 0; else return 0;
} }
static int cmp_desc_float(const void *a,const void *b) { static int cmp_desc_float(const void *a,const void *b) {
struct float_w_idx *a1 = (struct float_w_idx*)a; const struct float_w_idx *a1 = (const struct float_w_idx*)a;
struct float_w_idx *a2 = (struct float_w_idx*)b; const struct float_w_idx *a2 = (const struct float_w_idx*)b;
if((*a1).val>(*a2).val)return -1; if((*a1).val>(*a2).val)return -1;
else if((*a1).val<(*a2).val)return 1; else if((*a1).val<(*a2).val)return 1;
else return 0; else return 0;
} }
static int cmp_asc_int(const void *a,const void *b) { static int cmp_asc_int(const void *a,const void *b) {
struct int_w_idx *a1 = (struct int_w_idx*)a; const struct int_w_idx *a1 = (const struct int_w_idx*)a;
struct int_w_idx *a2 = (struct int_w_idx*)b; const struct int_w_idx *a2 = (const struct int_w_idx*)b;
if((*a1).val<(*a2).val)return -1; if((*a1).val<(*a2).val)return -1;
else if((*a1).val>(*a2).val)return 1; else if((*a1).val>(*a2).val)return 1;
else return 0; else return 0;
} }
static int cmp_desc_int(const void *a,const void *b) { static int cmp_desc_int(const void *a,const void *b) {
struct int_w_idx *a1 = (struct int_w_idx*)a; const struct int_w_idx *a1 = (const struct int_w_idx*)a;
struct int_w_idx *a2 = (struct int_w_idx*)b; const struct int_w_idx *a2 = (const struct int_w_idx*)b;
if((*a1).val>(*a2).val)return -1; if((*a1).val>(*a2).val)return -1;
else if((*a1).val<(*a2).val)return 1; else if((*a1).val<(*a2).val)return 1;
else return 0; else return 0;
@ -269,15 +233,6 @@ static void sort_int
free(data); free(data);
} }
static ch_vec3 cross(ch_vec3* v1, ch_vec3* v2)
{
ch_vec3 cross;
cross.x = v1->y * 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 */ /* calculates the determinent of a 4x4 matrix */
static CH_FLOAT det_4x4(CH_FLOAT* m) { static CH_FLOAT det_4x4(CH_FLOAT* m) {
return return
@ -819,178 +774,5 @@ void convhull_3d_build
free(A); free(A);
} }
void convhull_3d_export_obj
(
ch_vertex* const vertices,
const int nVert,
int* const faces,
const int nFaces,
const int keepOnlyUsedVerticesFLAG,
char* const obj_filename
)
{
int i, j;
char path[256] = "\0";
CV_STRNCPY(path, obj_filename, strlen(obj_filename));
FILE* obj_file;
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
CV_STRCAT(path, ".obj");
fopen_s(&obj_file, path, "wt");
#else
obj_file = fopen(strcat(path, ".obj"), "wt");
#endif
fprintf(obj_file, "o\n");
CH_FLOAT scale;
ch_vec3 v1, v2, normal;
/* export vertices */
if(keepOnlyUsedVerticesFLAG){
for (i = 0; i < nFaces; i++)
for(j=0; j<3; j++)
fprintf(obj_file, "v %f %f %f\n", vertices[faces[i*3+j]].x,
vertices[faces[i*3+j]].y, vertices[faces[i*3+j]].z);
}
else {
for (i = 0; i < nVert; i++)
fprintf(obj_file, "v %f %f %f\n", vertices[i].x,
vertices[i].y, vertices[i].z);
}
/* export the face normals */
for (i = 0; i < nFaces; i++){
/* calculate cross product between v1-v0 and v2-v0 */
v1 = vertices[faces[i*3+1]];
v2 = vertices[faces[i*3+2]];
v1.x -= vertices[faces[i*3]].x;
v1.y -= vertices[faces[i*3]].y;
v1.z -= vertices[faces[i*3]].z;
v2.x -= vertices[faces[i*3]].x;
v2.y -= vertices[faces[i*3]].y;
v2.z -= vertices[faces[i*3]].z;
normal = cross(&v1, &v2);
/* normalise to unit length */
scale = 1.0/(sqrt(pow(normal.x, 2.0)+pow(normal.y, 2.0)+pow(normal.z, 2.0))+2.23e-9);
normal.x *= scale;
normal.y *= scale;
normal.z *= scale;
fprintf(obj_file, "vn %f %f %f\n", normal.x, normal.y, normal.z);
}
/* export the face indices */
if(keepOnlyUsedVerticesFLAG){
for (i = 0; i < nFaces; i++){
/* vertices are in same order as the faces, and normals are in order */
fprintf(obj_file, "f %u//%u %u//%u %u//%u\n",
i*3 + 1, i + 1,
i*3+1 + 1, i + 1,
i*3+2 + 1, i + 1);
}
}
else {
/* just normals are in order */
for (i = 0; i < nFaces; i++){
fprintf(obj_file, "f %u//%u %u//%u %u//%u\n",
faces[i*3] + 1, i + 1,
faces[i*3+1] + 1, i + 1,
faces[i*3+2] + 1, i + 1);
}
}
fclose(obj_file);
}
void convhull_3d_export_m
(
ch_vertex* const vertices,
const int nVert,
int* const faces,
const int nFaces,
char* const m_filename
)
{
int i;
char path[256] = { "\0" };
memcpy(path, m_filename, strlen(m_filename));
FILE* m_file;
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
CV_STRCAT(path, ".m");
fopen_s(&m_file, path, "wt");
#else
m_file = fopen(strcat(path, ".m"), "wt");
#endif
/* save face indices and vertices for verification in matlab: */
fprintf(m_file, "vertices = [\n");
for (i = 0; i < nVert; i++)
fprintf(m_file, "%f, %f, %f;\n", vertices[i].x, vertices[i].y, vertices[i].z);
fprintf(m_file, "];\n\n\n");
fprintf(m_file, "faces = [\n");
for (int i = 0; i < nFaces; i++) {
fprintf(m_file, " %u, %u, %u;\n",
faces[3*i+0]+1,
faces[3*i+1]+1,
faces[3*i+2]+1);
}
fprintf(m_file, "];\n\n\n");
fclose(m_file);
}
void extractVerticesFromObjFile(char* const obj_filename, ch_vertex** out_vertices, int* out_nVert)
{
FILE* obj_file;
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
CV_STRCAT(obj_filename, ".obj");
fopen_s(&obj_file, obj_filename, "r");
#else
obj_file = fopen(strcat(obj_filename, ".obj"), "r");
#endif
/* determine number of vertices */
unsigned int nVert = 0;
char line[256];
while (fgets(line, sizeof(line), obj_file)) {
char* vexists = strstr(line, "v ");
if(vexists!=NULL)
nVert++;
}
(*out_nVert) = nVert;
(*out_vertices) = (ch_vertex*)malloc(nVert*sizeof(ch_vertex));
/* extract the vertices */
rewind(obj_file);
int i=0;
int vertID, prev_char_isDigit, current_char_isDigit;
char vert_char[256] = { 0 };
while (fgets(line, sizeof(line), obj_file)) {
char* vexists = strstr(line, "v ");
if(vexists!=NULL){
prev_char_isDigit = 0;
vertID = -1;
for(int j=0; j<strlen(line)-1; j++){
if(isdigit(line[j])||line[j]=='.'||line[j]=='-'||line[j]=='+'||line[j]=='E'||line[j]=='e'){
vert_char[strlen(vert_char)] = line[j];
current_char_isDigit = 1;
}
else
current_char_isDigit = 0;
if((prev_char_isDigit && !current_char_isDigit) || j ==strlen(line)-2 ){
vertID++;
if(vertID>4){
/* 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 */

View File

@ -1012,6 +1012,9 @@ public:
std::size_t size() const { std::size_t size() const {
return m_vertices.size(); return m_vertices.size();
} }
const Vector3& operator[]( std::size_t i ) const {
return m_vertices[i];
}
brushsplit_t classify_plane( const Plane3& plane ) const { brushsplit_t classify_plane( const Plane3& plane ) const {
brushsplit_t split; brushsplit_t split;
for( const_iterator i = begin(); i != end(); ++i ){ for( const_iterator i = begin(); i != end(); ++i ){
@ -1084,6 +1087,53 @@ public:
} }
}; };
#include "convhull_3d.h"
void CSG_build_hull( const MergeVertices& mergeVertices, MergePlanes& mergePlanes ){
#if 0
/* bruteforce new planes */
for( MergeVertices::const_iterator i = mergeVertices.begin() + 0; i != mergeVertices.end() - 2; ++i )
for( MergeVertices::const_iterator j = i + 1; j != mergeVertices.end() - 1; ++j )
for( MergeVertices::const_iterator k = j + 1; k != mergeVertices.end() - 0; ++k ){
const Plane3 plane = plane3_for_points( *i, *j, *k );
if( plane3_valid( plane ) ){
const brushsplit_t split = mergeVertices.classify_plane( plane );
if( ( split.counts[ePlaneFront] == 0 ) != ( split.counts[ePlaneBack] == 0 ) ){
if( split.counts[ePlaneFront] != 0 )
mergePlanes.insert( MergePlane( plane3_flipped( plane ), *i, *j, *k ) );
else
mergePlanes.insert( MergePlane( plane, *i, *k, *j ) );
}
}
}
#else
const int nVertices = mergeVertices.size();
ch_vertex* vertices = ( ch_vertex* )malloc( mergeVertices.size() * sizeof( ch_vertex ) );
for( std::size_t i = 0; i < mergeVertices.size(); ++i ){
vertices[i].x = static_cast<double>( mergeVertices[i].x() );
vertices[i].y = static_cast<double>( mergeVertices[i].y() );
vertices[i].z = static_cast<double>( mergeVertices[i].z() );
}
int* faceIndices = NULL;
int nFaces;
convhull_3d_build( vertices, nVertices, &faceIndices, &nFaces );
/* Where 'faceIndices' is a flat 2D matrix [nFaces x 3] */
for( int i = 0; i < nFaces; ++i ){
Vector3 p[3];
for( int j = 0; j < 3; ++j ){
// p[j] = Vector3( vertices[faceIndices[i * 3 + j]].x, vertices[faceIndices[i * 3 + j]].y, vertices[faceIndices[i * 3 + j]].z );
p[j] = mergeVertices[faceIndices[i * 3 + j]];
}
const Plane3 plane = plane3_for_points( p[0], p[1], p[2] );
if( plane3_valid( plane ) ){
mergePlanes.insert( MergePlane( plane, p[0], p[2], p[1] ) );
}
}
free( vertices );
free( faceIndices );
#endif
}
void CSG_WrapMerge( const ClipperPoints& clipperPoints ){ void CSG_WrapMerge( const ClipperPoints& clipperPoints ){
const bool primit = ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ); const bool primit = ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive );
brush_vector_t selected_brushes; brush_vector_t selected_brushes;
@ -1127,21 +1177,8 @@ void CSG_WrapMerge( const ClipperPoints& clipperPoints ){
mergePlanes.insert( MergePlane( face.getPlane().plane3(), &face ) ); mergePlanes.insert( MergePlane( face.getPlane().plane3(), &face ) );
} }
} }
/* bruteforce new planes */
for( MergeVertices::const_iterator i = mergeVertices.begin() + 0; i != mergeVertices.end() - 2; ++i ) CSG_build_hull( mergeVertices, mergePlanes );
for( MergeVertices::const_iterator j = i + 1; j != mergeVertices.end() - 1; ++j )
for( MergeVertices::const_iterator k = j + 1; k != mergeVertices.end() - 0; ++k ){
const Plane3 plane = plane3_for_points( *i, *j, *k );
if( plane3_valid( plane ) ){
const brushsplit_t split = mergeVertices.classify_plane( plane );
if( ( split.counts[ePlaneFront] == 0 ) != ( split.counts[ePlaneBack] == 0 ) ){
if( split.counts[ePlaneFront] != 0 )
mergePlanes.insert( MergePlane( plane3_flipped( plane ), *i, *j, *k ) );
else
mergePlanes.insert( MergePlane( plane, *i, *k, *j ) );
}
}
}
//globalOutputStream() << mergePlanes.size() << " mergePlanes.size()\n"; //globalOutputStream() << mergePlanes.size() << " mergePlanes.size()\n";
if( mergePlanes.size() < 4 ){ if( mergePlanes.size() < 4 ){
@ -1242,21 +1279,8 @@ void CSG_DeleteComponents(){
} }
} }
/* bruteforce new planes */ CSG_build_hull( mergeVertices, mergePlanes );
for( MergeVertices::const_iterator i = mergeVertices.begin() + 0; i != mergeVertices.end() - 2; ++i )
for( MergeVertices::const_iterator j = i + 1; j != mergeVertices.end() - 1; ++j )
for( MergeVertices::const_iterator k = j + 1; k != mergeVertices.end() - 0; ++k ){
const Plane3 plane = plane3_for_points( *i, *j, *k );
if( plane3_valid( plane ) ){
const brushsplit_t split = mergeVertices.classify_plane( plane );
if( ( split.counts[ePlaneFront] == 0 ) != ( split.counts[ePlaneBack] == 0 ) ){
if( split.counts[ePlaneFront] != 0 )
mergePlanes.insert( MergePlane( plane3_flipped( plane ), *i, *j, *k ) );
else
mergePlanes.insert( MergePlane( plane, *i, *k, *j ) );
}
}
}
if( mergePlanes.size() < 4 ){ if( mergePlanes.size() < 4 ){
globalWarningStream() << "CSG_DeleteComponents: Too few planes left: " << mergePlanes.size() << ".\n"; globalWarningStream() << "CSG_DeleteComponents: Too few planes left: " << mergePlanes.size() << ".\n";
return; return;