123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782 |
- #include "pch.h"
- const int c_maxVertices = 10000;
- const int c_maxFaces = 1000;
- const int c_cbBfr = 512;
- const int c_ellipse = 0x01;
- const int c_oneHull = 0x02;
- const int c_writeHull = 0x04;
- const int c_writeMesh = 0x08;
- const int c_startMesh = 0x10;
- const int c_endMesh = 0x20;
- bool x2pl(const char* fileIn, const char* fileOut);
- bool qh2cvh(const char* file, FILE* fOut, int mode);
- bool dat2cvh(const char* file, FILE* fOut);
- int main(int argc, char** argv)
- {
- bool success = false;
- if (argc == 3)
- {
- char bfr[256];
- strcpy(bfr, argv[2]);
- strcat(bfr, ".cvh");
- FILE* fOut = fopen(bfr, "w");
- if (fOut)
- {
- enum Modes
- {
- c_flatten,
- c_extract,
- c_bound
- } mode;
- if (strcmp(argv[1], "-flatten") == 0)
- mode = c_flatten;
- else if (strcmp(argv[1], "-extract") == 0)
- mode = c_extract;
- else if (strcmp(argv[1], "-bound") == 0)
- mode = c_bound;
- char bfrIn[c_cbBfr];
- strcpy(bfrIn, argv[2]);
- strcat(bfrIn, "flat.x");
- char bfrOut[c_cbBfr];
- strcpy(bfrOut, argv[2]);
- strcat(bfrOut, ".pl");
- success = x2pl(bfrIn, bfrOut);
- if (success)
- {
- char bfrSystem[c_cbBfr];
- strcpy(bfrSystem, "%fedroot%\\src\\bin\\qhull -o -Tv <");
- strcat(bfrSystem, argv[2]);
- strcat(bfrSystem, ".pl >");
- strcat(bfrSystem, argv[2]);
- strcat(bfrSystem, ".qh");
- system(bfrSystem);
- success = qh2cvh(argv[2], fOut, (mode == c_bound)
- ? (c_ellipse | c_startMesh)
- : (c_ellipse | c_oneHull | c_writeHull | c_writeMesh | c_startMesh | c_endMesh));
- }
- if (mode == c_bound)
- {
- int hullID = -1;
- do
- {
- char bfrHull[c_cbBfr];
- sprintf(bfrHull, "%s_%d", argv[2], ++hullID);
- char bfrIn[c_cbBfr];
- strcpy(bfrIn, bfrHull);
- strcat(bfrIn, ".mesh");
- char bfrOut[c_cbBfr];
- strcpy(bfrOut, bfrHull);
- strcat(bfrOut, ".pl");
- success = x2pl(bfrIn, bfrOut);
- }
- while (success);
- success = true;
- fprintf(fOut, "%5d\n", hullID);
- for (int i = 0; ((i < hullID) && success); i++)
- {
- char bfrHull[c_cbBfr];
- sprintf(bfrHull, "%s_%d", argv[2], i);
- char bfrSystem[c_cbBfr];
- strcpy(bfrSystem, "%fedroot%\\src\\bin\\qhull -o -Tv <");
- strcat(bfrSystem, bfrHull);
- strcat(bfrSystem, ".pl >");
- strcat(bfrSystem, bfrHull);
- strcat(bfrSystem, ".qh");
- system(bfrSystem);
- success = qh2cvh(bfrHull, fOut, (i == hullID - 1)
- ? (c_writeHull | c_writeMesh | c_endMesh)
- : (c_writeHull | c_writeMesh));
- }
- }
- if (mode != c_flatten)
- success = dat2cvh(argv[2], fOut);
- fclose(fOut);
- }
- }
- return success ? 0 : -1;
- }
- bool x2pl(const char* fileIn, const char* fileOut)
- {
- bool success = false;
- FILE* fIn = NULL;
- FILE* fOut = NULL;
- fIn = fopen(fileIn, "r");
- if (fIn)
- {
- fOut = fopen(fileOut, "w");
- if (fOut)
- {
- success = true;
- const int c_cbLine = 512;
- char line[c_cbLine];
- int nVertices = -2;
- while (fgets(line, c_cbLine, fIn) != NULL)
- {
- if (nVertices == -2)
- {
- if (strcmp(line, "Mesh {\n"/*}*/) == 0)
- nVertices = -1;
- }
- else if (nVertices == -1)
- {
- nVertices = atoi(line);
- fprintf(fOut, "3 %d %s\n", nVertices, fileIn);
- }
- else if (nVertices == 0)
- break;
- else
- {
- //Write out the vertices, translating ';' to ' '
- int nsc = 0;
- const char* p = line;
- while (true)
- {
- assert (*p != '\0');
- if (*p == ';')
- {
- if (++nsc == 3)
- break;
- fputc('\t', fOut);
- }
- else
- fputc(*p, fOut);
- p++;
- }
- fputc('\n', fOut);
- nVertices--;
- }
- }
- fclose(fOut);
- }
- fclose(fIn);
- }
- return success;
- }
- struct Vertex
- {
- Vertex(void)
- :
- cUsed(0)
- {
- }
-
- double xyz[3];
- int id;
- int cUsed;
- };
- struct Face
- {
- int ids[3];
- };
- void swap(double g[3][4], int i, int j)
- {
- for (int k = 0; (k < 4); k++)
- {
- double t = g[i][k];
- g[i][k] = g[j][k];
- g[j][k] = t;
- }
- }
- void subtract(double g[3][4], double f, int i, int j)
- {
- for (int k = 0; (k < 4); k++)
- {
- g[i][k] -= g[j][k] * f;
- }
- }
- bool reduce(double g[3][4])
- {
- if (fabs(g[1][0]) > fabs(g[0][0]))
- swap(g, 0, 1);
- if (fabs(g[2][0]) > fabs(g[0][0]))
- swap(g, 0, 2);
- if (fabs(g[0][0]) < 1.0e-4)
- return false;
- //Reduce the first column
- subtract(g, g[1][0] / g[0][0], 1, 0);
- g[1][0] = 0.0;
- subtract(g, g[2][0] / g[0][0], 2, 0);
- g[2][0] = 0.0;
- if (fabs(g[2][1]) > fabs(g[1][1]))
- swap(g, 1, 2);
- if (fabs(g[1][1]) < 1.0e-4)
- return false;
- //Reduce the second column
- subtract(g, g[2][1] / g[1][1], 2, 1);
- g[2][1] = 0.0;
- //Back solve ...
- if (fabs(g[2][2]) < 1.0e-4)
- return false;
- g[2][3] = g[2][3] / g[2][2];
- g[2][2] = 1.0;
- subtract(g, g[1][2], 1, 2);
- g[1][2] = 0.0f;
- subtract(g, g[0][2], 0, 2);
- g[0][2] = 0.0f;
- assert (g[1][1] != 0.0f);
- g[1][3] = g[1][3] / g[1][1];
- g[1][1] = 1.0;
- subtract(g, g[0][1], 0, 1);
- assert (g[0][0] != 0.0f);
- g[0][3] = g[0][3] / g[0][0];
- g[0][0] = 1.0;
- return true;
- }
- Vertex vertices[c_maxVertices];
- Face faces[c_maxFaces];
- int vertexIDs[c_maxVertices];
- void addAdjacent(int* nAdjacent,
- int adjacent[],
- int id)
- {
- if (vertices[id].cUsed > 2)
- {
- for (int i = 0; (i < *nAdjacent); i++)
- {
- if (adjacent[i] == id)
- return;
- }
- adjacent[(*nAdjacent)++] = id;
- }
- }
- bool qh2cvh(const char* file, FILE* fOut, int mode)
- {
- bool success = false;
- assert (fOut);
- FILE* fIn;
- {
- char bfr[c_cbBfr];
- assert (strlen(file) < c_cbBfr - 10);
- strcpy(bfr, file);
- strcat(bfr, ".qh");
- fIn = fopen(bfr, "r");
- }
- if (fIn)
- {
- success = true;
- int nVertices = 0;
- int nUsedVertices = 0;
- int nSingularFaces = 0;
- int nFaces = 0;
- const int c_cbLine = 512;
- char line[c_cbLine];
- fgets(line, c_cbLine, fIn); //Skip 1st line
- fgets(line, c_cbLine, fIn);
- sscanf(line, "%d %d", &nVertices, &nFaces);
- //Read in all of the vertices
- {
- for (int i = 0; (i < nVertices); i++)
- {
- fgets(line, c_cbLine, fIn);
- sscanf(line, "%lf %lf %lf",
- &(vertices[i].xyz[0]),
- &(vertices[i].xyz[1]),
- &(vertices[i].xyz[2]));
- vertices[i].cUsed = 0;
- }
- }
- //Read in all of the faces
- {
- int faceID = 0; //possibly more than one face/line
- for (int i = 0; (i < nFaces); i++)
- {
- fgets(line, c_cbLine, fIn);
- int n = atoi(line);
- const int c_maxIDs = 20;
- assert (n >= 3);
- assert (n < c_maxIDs);
- int ids[c_maxIDs];
- {
- const char* p = line;
- for (int j = 0; (j < n); j++)
- {
- p = strchr(p, ' ');
- assert (p);
- ids[j] = atoi(++p);
- }
- }
- if ((n & 0x01) == 1)
- {
- //Odd number of vertices
- //Remove the last vertex from consideration
- faces[faceID].ids[0] = ids[n-2];
- faces[faceID].ids[1] = ids[n-1];
- faces[faceID].ids[2] = ids[0];
- faceID++;
- n--;
- }
- assert ((n & 0x01) == 0);
- int p0 = 0;
- int p1 = 1;
- int p2 = n - 2;
- int p3 = n - 1;
- while (p2 > p1)
- {
- faces[faceID].ids[0] = ids[p0];
- faces[faceID].ids[1] = ids[p1];
- faces[faceID].ids[2] = ids[p2];
- faceID++;
- faces[faceID].ids[0] = ids[p2];
- faces[faceID].ids[1] = ids[p3];
- faces[faceID].ids[2] = ids[p0];
- faceID++;
- p0++;
- p1++;
- p2--;
- p3--;
- }
- }
- nFaces = faceID;
- }
- //Mark the vertices actually used by faces
- {
- for (int i = 0; (i < nFaces); i++)
- {
- for (int j = 0; (j < 3); j++)
- vertices[faces[i].ids[j]].cUsed++;
- }
- }
- //Generate IDs for the vertices that are used
- {
- int vertexID = 0;
- for (int i = 0; (i < nVertices); i++)
- {
- if (vertices[i].cUsed > 2)
- {
- vertexIDs[vertexID] = i;
- vertices[i].id = vertexID++;
- }
- else
- nSingularFaces += vertices[i].cUsed;
- }
- nUsedVertices = vertexID;
- }
- double radius = 0.0;
- double xyzMin[3] = {0.0, 0.0, 0.0};
- double xyzMax[3] = {0.0, 0.0, 0.0};
- {
- //Find the most distance vertex in the model
- for (int i = 0; (i < nUsedVertices); i++)
- {
- int vertexID = vertexIDs[i];
- double r = vertices[vertexID].xyz[0] * vertices[vertexID].xyz[0] +
- vertices[vertexID].xyz[1] * vertices[vertexID].xyz[1] +
- vertices[vertexID].xyz[2] * vertices[vertexID].xyz[2];
- if (r > radius)
- radius = r;
- for (int j = 0; (j < 3); j++)
- {
- if (vertices[vertexID].xyz[j] > xyzMax[j])
- xyzMax[j] = vertices[vertexID].xyz[j];
- if (vertices[vertexID].xyz[j] < xyzMin[j])
- xyzMin[j] = vertices[vertexID].xyz[j];
- }
- }
- }
- radius = sqrt(radius);
- static FILE* fOutMesh;
- if (mode & c_startMesh)
- {
- char bfr[c_cbBfr];
- assert (strlen(file) < c_cbBfr - 10);
- strcpy(bfr, "..\\artbuild\\");
- strcat(bfr, file);
- strcat(bfr, "_m.x");
- fOutMesh = fopen(bfr, "w");
- fprintf(fOutMesh, "xof 0302txt 0064\nHeader { 1; 0; 1; }\n");
- }
- if (mode & c_writeMesh)
- {
- fprintf(fOutMesh, "Mesh\n{\n%d;\n", nUsedVertices); //}
- //Only print the vertices actually used by the convex hull
- {
- bool firstF = true;
- for (int i = 0; (i < nVertices); i++)
- {
- if (vertices[i].cUsed > 2)
- {
- if (firstF)
- firstF = false;
- else
- fputs(",\n", fOutMesh);
- fprintf(fOutMesh, "%14.6f;%14.6f;%14.6f;",
- -vertices[i].xyz[0],
- -vertices[i].xyz[1],
- vertices[i].xyz[2]);
- }
- }
- }
- //Print the faces with the correct vertex IDs
- {
- fprintf(fOutMesh, ";\n\n%d;\n", nFaces - nSingularFaces);
- bool first = true;
- for (int i = 0; (i < nFaces); i++)
- {
- if ((vertices[faces[i].ids[2]].cUsed > 2) &&
- (vertices[faces[i].ids[1]].cUsed > 2) &&
- (vertices[faces[0].ids[1]].cUsed > 2))
- {
- if (first)
- first = false;
- else
- fputs(",\n", fOutMesh);
- fprintf(fOutMesh, "3;%d,%d,%d;",
- vertices[faces[i].ids[2]].id,
- vertices[faces[i].ids[1]].id,
- vertices[faces[i].ids[0]].id);
- }
- }
- fputs(";\n", fOutMesh);
- }
- fprintf(fOutMesh, /*{*/"}\n");
- if (mode & c_endMesh)
- fclose(fOutMesh);
- }
- if (mode & c_ellipse)
- {
- //See if the bounding box is appropriately centered
- {
- int i;
- for (i = 0; (i < 3); i++)
- {
- if (fabs(xyzMin[i] + xyzMax[i]) > 0.05 * (xyzMax[i] - xyzMin[i]))
- break;
- }
- if (i != 3)
- {
- printf("***Non-centered cvh: %s\n", file);
- printf("\t%f %f\n", fabs(xyzMin[0] + xyzMax[0]), (xyzMax[0] - xyzMin[0]));
- printf("\t%f %f\n", fabs(xyzMin[1] + xyzMax[1]), (xyzMax[1] - xyzMin[1]));
- printf("\t%f %f\n", fabs(xyzMin[2] + xyzMax[2]), (xyzMax[2] - xyzMin[2]));
- }
- }
- //First attempt: find the best sphere
- double aBest = radius;
- double bBest = radius;
- double cBest = radius;
- double vBest = aBest * bBest * cBest;
- //Second attempt: find the best ellipse with the same aspect ratio as the bounding box
- {
-
- double a = fabs(xyzMin[0]) > fabs(xyzMax[0]) ? fabs(xyzMin[0]) : fabs(xyzMax[0]);
- double b = fabs(xyzMin[1]) > fabs(xyzMax[1]) ? fabs(xyzMin[1]) : fabs(xyzMax[1]);
- double c = fabs(xyzMin[2]) > fabs(xyzMax[2]) ? fabs(xyzMin[2]) : fabs(xyzMax[2]);
- double a2 = a * a;
- double b2 = b * b;
- double c2 = c * c;
- //Now find the fudge factor to make everything "fit"
- double rMax = 0.0;
- for (int i = 0; (i < nUsedVertices); i++)
- {
- int vertexID = vertexIDs[i];
- double r = (vertices[vertexID].xyz[0] * vertices[vertexID].xyz[0]) / a2 +
- (vertices[vertexID].xyz[1] * vertices[vertexID].xyz[1]) / b2 +
- (vertices[vertexID].xyz[2] * vertices[vertexID].xyz[2]) / c2;
- if (r > rMax)
- rMax = r;
- }
- rMax = sqrt(rMax);
- a *= rMax;
- b *= rMax;
- c *= rMax;
- double v = a * b * c;
- if (v < vBest)
- {
- vBest = v;
- aBest = a;
- bBest = b;
- cBest = c;
- }
- }
- //Third attempt ... try to find a better ellipse from the data
- {
- //Generate the best ellipse (minimum volume) ellipse that contains all of the points.
- //Do it the real expensive way: look at all point triples and deduce the radii of the axes
- //and then pick the one with the smallest volume
- //Ellipse equation:
- // (x/a)^2 + (y/b)^2 + (z/c)^2 = 1
- //We actually want to solve:
- // Minimize: 1.0 / (ra * rb * rc) such that
- // ra * x1^2 + rb * y1^2 + rc * z1^2 <= 1
- // ...
- // ra * xN^2 + rb * yN^2 + rc * zN^2 <= 1
- //but since it is too early in the morning to crank out a simplex algorithm, fake it:
- //Try all sets of 3 points, find the corresponding ellipse and see if it contains all the
- //other points.
- double* xyzs[3];
- for (int i = 0; (i < nUsedVertices - 2); i++)
- {
- xyzs[0] = vertices[vertexIDs[i]].xyz;
- for (int j = i + 1; (j < nUsedVertices - 1); j++)
- {
- xyzs[1] = vertices[vertexIDs[j]].xyz;
- for (int k = j + 1; (k < nUsedVertices); k++)
- {
- xyzs[2] = vertices[vertexIDs[k]].xyz;
- //Rewrite the ellipse equation
- //from: (x/a)^2 + (y/b)^2 + (z/c)^2 = 1
- //to: ra x^2 + rb y^2 + rc z^2 = 1 ra = 1.0/a^2; a = sqrt(1.0/ra);
- //and solve for xyz1, 2 & 3.
- //Setup the gaussian matrix (? ... boy, it has been awhile)
- double g[3][4];
- {
- for (int m = 0; (m < 3); m++)
- {
- for (int n = 0; (n < 3); n++)
- g[m][n] = xyzs[m][n] * xyzs[m][n];
- g[m][3] = 1.0f;
- }
- }
- //Reduce the gaussian
- if (reduce(g) &&
- (g[0][3] > 1.0e-4) &&
- (g[1][3] > 1.0e-4) &&
- (g[2][3] > 1.0e-4))
- {
- double a = sqrt(1.0 / g[0][3]);
- double b = sqrt(1.0 / g[1][3]);
- double c = sqrt(1.0 / g[2][3]);
- double v = a * b * c;
- if (v < vBest)
- {
- //Vertify that all of the other points in the convex hull are properly contained.
- for (int m = 0; (m < nUsedVertices); m++)
- {
- double x = vertices[vertexIDs[m]].xyz[0] / a;
- double y = vertices[vertexIDs[m]].xyz[1] / b;
- double z = vertices[vertexIDs[m]].xyz[2] / c;
- if (x*x + y*y + z*z > 1.01f) //Allow a bit of fudging
- break;
- }
- if (m == nUsedVertices)
- {
- vBest = v;
- aBest = a;
- bBest = b;
- cBest = c;
- }
- }
- }
- }
- }
- }
- }
- fprintf(fOut, "%16.8f%16.8f%16.8f%16.8f%16.8f\n",
- radius,
- aBest, bBest, cBest, ((aBest > bBest)
- ? ((aBest > cBest) ? aBest : cBest)
- : ((bBest > cBest) ? bBest : cBest)) / radius);
- }
- if (mode & c_oneHull)
- fprintf(fOut, "1\n");
- //Only print the vertices actually used by the convex hull
- if (mode & c_writeHull)
- {
- int nAdjacencies = 2 * (nUsedVertices + nFaces - nSingularFaces - 2);
- fprintf(fOut, "%5d%5d%18.8f%18.8f%18.8f\n",
- nUsedVertices, nAdjacencies,
- 0.5 * (xyzMin[0] + xyzMax[0]),
- 0.5 * (xyzMin[1] + xyzMax[1]),
- 0.5 * (xyzMin[2] + xyzMax[2]));
- bool firstF = true;
- for (int vertexID = 0; (vertexID < nVertices); vertexID++)
- {
- if (vertices[vertexID].cUsed > 2)
- {
- fprintf(fOut, "%18.8f%18.8f%18.8f",
- vertices[vertexID].xyz[0],
- vertices[vertexID].xyz[1],
- vertices[vertexID].xyz[2]);
- //print the adjacent vertices
- const int c_maxAdjacent = c_maxVertices;
- int adjacent[c_maxAdjacent];
- int nAdjacent = 0;
- for (int faceID = 0; (faceID < nFaces); faceID++)
- {
- //Does this face contain this vertex?
- if (vertexID == faces[faceID].ids[0])
- {
- addAdjacent(&nAdjacent, adjacent, faces[faceID].ids[1]);
- addAdjacent(&nAdjacent, adjacent, faces[faceID].ids[2]);
- }
- else if (vertexID == faces[faceID].ids[1])
- {
- addAdjacent(&nAdjacent, adjacent, faces[faceID].ids[0]);
- addAdjacent(&nAdjacent, adjacent, faces[faceID].ids[2]);
- }
- else if (vertexID == faces[faceID].ids[2])
- {
- addAdjacent(&nAdjacent, adjacent, faces[faceID].ids[0]);
- addAdjacent(&nAdjacent, adjacent, faces[faceID].ids[1]);
- }
- }
- fprintf(fOut, "%5d\n", nAdjacent);
- for (int a = 0; (a < nAdjacent); a++)
- fprintf(fOut, "%5d", vertices[adjacent[a]].id);
- fputc('\n', fOut);
-
- nAdjacencies -= nAdjacent;
- }
- }
- if (nAdjacencies != 0)
- {
- printf("Error constructing %s.cvh\n", file);
- success = false;
- }
- }
- fclose(fIn);
- }
- return success;
- }
- bool dat2cvh(const char* file, FILE* fOut)
- {
- bool success = false;
- char bfr[c_cbBfr];
- strcpy(bfr, file);
- strcat(bfr, ".dat");
- FILE* fIn = fopen(bfr, "r");
- if (fIn)
- {
- success = true;
- char line[c_cbBfr];
- while (fgets(line, c_cbBfr, fIn) != NULL)
- {
- fputs(line, fOut);
- }
- fclose(fIn);
- }
- return success;
- }
|