1 /*************************************************************************
2 * *
3 * Vega FEM Simulation Library Version 3.1 *
4 * *
5 * "distance field" library , Copyright (C) 2007 CMU, 2016 USC *
6 * All rights reserved. *
7 * *
8 * Code authors: Jernej Barbic, Hongyi Xu, Yijing Li *
9 * http://www.jernejbarbic.com/code *
10 * *
11 * Research: Jernej Barbic, Hongyi Xu, Doug L. James *
12 * *
13 * Funding: National Science Foundation, Link Foundation, *
14 * Zumberge Research and Innovation Fund at USC *
15 * *
16 * This library is free software; you can redistribute it and/or *
17 * modify it under the terms of the BSD-style license that is *
18 * included with this library in the file LICENSE.txt *
19 * *
20 * This library is distributed in the hope that it will be useful, *
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file *
23 * LICENSE.TXT for more details. *
24 * *
25 *************************************************************************/
26
27 /*
28 This code generates a distance filed, either signed or unsigned,
29 to the given triangle mesh.
30
31 The distance field can be loaded/saved to a file.
32 You can also lookup the field (once computed or loaded) at arbitrary
33 locations inside the field, using trilinear interpolation.
34
35 Input mesh need not have triangle faces (e.g., can have quads;
36 they will be triangulated).
37
38 For signed field generation, the input mesh must be a closed manifold mesh.
39
40 Input mesh must be given in the .obj format:
41 http://www.royriggs.com/obj.html
42
43 By default, the bounding box will be a cube, obtained by fitting the
44 smallest cube to the geometry, and then expanded (scaled) from its
45 center by a factor of 1.5. You can provide your own bounding boxes.
46 However, note: (1) the provided bounding box must cover all the geometry,
47 and (2) bounding boxes that are not cubes were not (yet) tested.
48
49 The bounding box will be divided into a "resolution" number of
50 cubes ("voxels") along each axis. The distance field will be computed at the
51 vertices of these voxels. So, if resolution is 256, the bounding box
52 will get divided into 256 x 256 x 256 voxels, and the distance field
53 will be computed on the resulting 257 x 257 x 257 grid of voxel vertices.
54 Note that when indexing voxels, the indices (i,j,k) will run from 0 to 255
55 inclusive, whereas when indexing voxel vertices (also called "grid vertices"),
56 they will run from 0 to 256 inclusive.
57
58 Distance field data is stored at voxel vertices.
59 In memory, distance field value at voxel vertex (i,j,k) is stored
60 at location k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i .
61
62 Internally, the code builds an octree on top of the triangle mesh.
63 There are two parameters that control this process (you can keep them
64 at default values, which worked well in practice for us) :
65 the max depth of the octree is "maxDepth", and
66 the max number of triangles intersecting an octree cell is "maxTriCount".
67 Note: once max depth level is reached, the maxTriCount bound is not imposed any more.
68 */
69
70 #include <stdlib.h>
71 #include <stdio.h>
72 #include <string.h>
73 #include <float.h>
74 #include <fstream>
75 #include <set>
76 #include "triangle.h"
77 #include "triple.h"
78 #include "boundingBox.h"
79 #include "distanceField.h"
80 #include "trilinearInterpolation.h"
81 #include "vegalong.h"
82 using namespace std;
83
84 //#define GENERATE_DEBUG_DATA
85
GetFilesize(const char * filename)86 vegalong DistanceField::GetFilesize(const char *filename)
87 {
88 FILE * f = fopen(filename, "rb"); /* open the file in read only */
89
90 vegalong size = 0;
91 if (fseek(f,0,SEEK_END)==0) /* seek was successful */
92 size = ftell(f);
93 fclose(f);
94 return size;
95 }
96
DistanceField()97 DistanceField::DistanceField() : DistanceFieldBase()
98 {
99 distanceData = NULL;
100 pseudoData = NULL;
101 computeVoronoiDiagram = false;
102 voronoiDiagram = NULL;
103 minBoundaryDistance = 0;
104 floodFillTag = NULL;
105 }
106
DistanceField(int resolutionX_,int resolutionY_,int resolutionZ_)107 DistanceField::DistanceField(int resolutionX_, int resolutionY_, int resolutionZ_): DistanceFieldBase(resolutionX_, resolutionY_, resolutionZ_)
108 {
109 distanceData = NULL;
110 pseudoData = NULL;
111 computeVoronoiDiagram = false;
112 voronoiDiagram = NULL;
113 minBoundaryDistance = 0;
114 floodFillTag = NULL;
115
116 distanceData = (float*) malloc (sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
117 }
118
~DistanceField()119 DistanceField::~DistanceField()
120 {
121 free(distanceData);
122 free(pseudoData);
123 free(floodFillTag);
124 free(voronoiDiagram);
125 }
126
enableVoronoiDiagramComputation(bool computeVoronoiDiagram)127 void DistanceField::enableVoronoiDiagramComputation(bool computeVoronoiDiagram)
128 {
129 this->computeVoronoiDiagram = computeVoronoiDiagram;
130 }
131
132 // the routines for signed and unsigned distance field computation
133 #define COMPUTE_SIGNED_FIELD
134 #define COMPUTE_FLOOD_FIELD
135 #include "computeField.cpp"
136 #undef COMPUTE_FLOOD_FIELD
137 #include "computeField.cpp"
138 #undef COMPUTE_SIGNED_FIELD
139 #include "computeField.cpp"
140
load(const std::string & filename)141 int DistanceField::load(const std::string& filename)
142 {
143 ifstream fin(filename.c_str(),ios::binary);
144 if (!fin)
145 return 1;
146
147 fin.read((char*)&resolutionX, sizeof(int));
148
149 // the type of data (single-precision or double-precision) is encoded
150 // as the sign of the x-resolution
151 bool floatData = (resolutionX < 0);
152 if (floatData)
153 resolutionX = -resolutionX;
154
155 fin.read((char*)&resolutionY, sizeof(int));
156
157 if (resolutionY < 0) // negative second resolution implies closest point data
158 return 1;
159
160 fin.read((char*)&resolutionZ, sizeof(int));
161
162 fin.read((char*)&(bmin_[0]), sizeof(double));
163 fin.read((char*)&(bmin_[1]), sizeof(double));
164 fin.read((char*)&(bmin_[2]), sizeof(double));
165
166 fin.read((char*)&(bmax_[0]), sizeof(double));
167 fin.read((char*)&(bmax_[1]), sizeof(double));
168 fin.read((char*)&(bmax_[2]), sizeof(double));
169
170 setGridParameters();
171
172 distanceData = (float*) realloc (distanceData, sizeof(float) * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1));
173
174 float * distanceDataPos = distanceData;
175 int sliceResolution = (resolutionX+1) * (resolutionY+1);
176 int unitSize = floatData ? sizeof(float) : sizeof(double);
177 for(int k=0; k <= resolutionZ; k++, distanceDataPos += sliceResolution)
178 fin.read((char*)distanceDataPos, unitSize * sliceResolution);
179 //printf("\n");
180 fin.close();
181
182 return 0;
183 }
184
openStreamDistanceField(const std::string & filename,Vec3d * bmin,Vec3d * bmax,int * resolutionX,int * resolutionY,int * resolutionZ,bool * floatData,ifstream & fin)185 int DistanceField::openStreamDistanceField(const std::string& filename, Vec3d * bmin, Vec3d * bmax, int * resolutionX, int * resolutionY, int * resolutionZ, bool * floatData, ifstream & fin)
186 {
187 fin.open(filename.c_str(),ios::binary);
188 if (!fin)
189 return 1;
190
191 fin.read((char*)resolutionX, 4);
192
193 // the type of data (single-precision or double-precision) is encoded
194 // as the sign of the x-resolution
195 *floatData = (*resolutionX < 0);
196 if (*floatData)
197 *resolutionX = - *resolutionX;
198
199 fin.read((char*)resolutionY, 4);
200
201 if (*resolutionY < 0) // negative second resolution implies closest point data
202 return 1;
203
204 fin.read((char*)resolutionZ, 4);
205
206 double bminx, bminy, bminz;
207 fin.read((char*)&bminx, 8);
208 fin.read((char*)&bminy, 8);
209 fin.read((char*)&bminz, 8);
210
211 double bmaxx, bmaxy, bmaxz;
212 fin.read((char*)&bmaxx, 8);
213 fin.read((char*)&bmaxy, 8);
214 fin.read((char*)&bmaxz, 8);
215
216 (*bmin)[0] = bminx;
217 (*bmin)[1] = bminy;
218 (*bmin)[2] = bminz;
219
220 (*bmax)[0] = bmaxx;
221 (*bmax)[1] = bmaxy;
222 (*bmax)[2] = bmaxz;
223
224 return 0;
225 }
226
retrieveZSlice(ifstream & fin,bool floatData,int resolutionX,int resolutionY,int resolutionZ,float * slice)227 void DistanceField::retrieveZSlice(ifstream & fin, bool floatData, int resolutionX, int resolutionY, int resolutionZ, float * slice)
228 {
229 double * buffer = (double*) malloc (sizeof(double) * (resolutionX+1) * (resolutionY+1)); // place for one slice
230
231 float * distanceDataPos = slice;
232
233 if (floatData)
234 fin.read((char*)buffer, sizeof(float) * (resolutionX+1) * (resolutionY+1));
235 else
236 fin.read((char*)buffer, sizeof(double) * (resolutionX+1) * (resolutionY+1));
237
238 char * bufferPos = (char*)buffer;
239 int bufferPosInc = floatData ? sizeof(float) : sizeof(double);
240
241 // copy data to the slice
242 for(int j = 0; j <= resolutionY; j++)
243 for(int i = 0; i <= resolutionX; i++)
244 {
245 if (floatData)
246 *distanceDataPos = *(float*)bufferPos;
247 else
248 *distanceDataPos = *(double*)bufferPos;
249 distanceDataPos++;
250 bufferPos += bufferPosInc;
251 }
252 }
253
set(int resolutionX,int resolutionY,int resolutionZ,Vec3d bmin_,Vec3d bmax_,float * distanceData)254 void DistanceField::set(int resolutionX, int resolutionY, int resolutionZ, Vec3d bmin_, Vec3d bmax_, float * distanceData)
255 {
256 this->resolutionX = resolutionX;
257 this->resolutionY = resolutionY;
258 this->resolutionZ = resolutionZ;
259 this->bmin_ = bmin_;
260 this->bmax_ = bmax_;
261
262 setGridParameters();
263
264 free(this->distanceData);
265 this->distanceData = (float*) malloc (sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
266 memcpy(this->distanceData, distanceData, sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
267 }
268
isDoublePrecision(const std::string & filename,bool & doublePrecision)269 int DistanceField::isDoublePrecision(const std::string & filename, bool & doublePrecision)
270 {
271 ifstream fin(filename.c_str(),ios::binary);
272 if (!fin)
273 return 1;
274
275 int res;
276 fin.read((char*)&res,4);
277
278 // the type of data (single-precision or double-precision) is encoded
279 // as the sign of the x-resolution
280 doublePrecision = (res >= 0);
281
282 fin.close();
283
284 return 0;
285 }
286
saveToText(const std::string & filename)287 int DistanceField::saveToText(const std::string& filename)
288 {
289 FILE * fout = fopen((char*)filename.c_str(), "w");
290 if (!fout)
291 return 1;
292
293 fprintf(fout, "%d\n", resolutionX);
294 fprintf(fout, "%d\n", resolutionY);
295 fprintf(fout, "%d\n", resolutionZ);
296
297 for(int i=0; i<=resolutionX; i++)
298 for(int j=0; j<=resolutionY; j++)
299 for(int k=0; k<=resolutionZ; k++)
300 {
301 fprintf(fout,"%G\n", distance(i,j,k));
302 }
303
304 fclose(fout);
305
306 return 0;
307 }
308
save(const std::string & filename,bool doublePrecision)309 int DistanceField::save(const std::string& filename, bool doublePrecision)
310 {
311 if (doublePrecision)
312 printf("Error: double precision output is not supported. Using float instead.\n");
313 doublePrecision = false;
314
315 ofstream fout(filename.c_str(),ios::binary);
316
317 int data = resolutionX;
318 if (!doublePrecision)
319 data = -data;
320
321 fout.write((char*)&data,sizeof(int));
322 fout.write((char*)&resolutionY,sizeof(int));
323 fout.write((char*)&resolutionZ,sizeof(int));
324
325 fout.write((char*)&(bmin_[0]),sizeof(double));
326 fout.write((char*)&(bmin_[1]),sizeof(double));
327 fout.write((char*)&(bmin_[2]),sizeof(double));
328
329 fout.write((char*)&(bmax_[0]),sizeof(double));
330 fout.write((char*)&(bmax_[1]),sizeof(double));
331 fout.write((char*)&(bmax_[2]),sizeof(double));
332
333 // float precision
334 //fout.write((char*)distanceData,sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1)); // this version sometimes resulted in the field incorrectly written to file (unclear why)
335
336 for(int k=0; k<=resolutionZ; k++)
337 for(int j=0; j<=resolutionY; j++)
338 for(int i=0; i<=resolutionX; i++)
339 {
340 fout.write((char*)&distanceData[k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i], sizeof(float));
341 }
342
343 /*
344 if (doublePrecision)
345 fout.write((char*)distanceData,sizeof(double)*(resolution+1)*(resolution+1)*(resolution+1));
346 else
347 {
348 float * buffer;
349 convertToFloat(&buffer);
350 fout.write((char*)buffer,sizeof(float)*(resolution+1)*(resolution+1)*(resolution+1));
351 free(buffer);
352 }
353 */
354 fout.close();
355
356 #ifdef GENERATE_DEBUG_DATA
357 ofstream fout1("debugPseudo", ios::binary);
358 fout1.write((char*)pseudoData, 6*sizeof(float)*(resolution+1)*(resolution+1)*(resolution+1));
359 fout1.close();
360 #endif
361
362 return 0;
363 }
364
saveVoronoiDiagram(const std::string & filename)365 int DistanceField::saveVoronoiDiagram(const std::string& filename)
366 {
367 if (voronoiDiagram == NULL)
368 {
369 printf("Error in saveVoronoiDiagram: Voronoi diagram has not been computed.\n");
370 return 1;
371 }
372
373 ofstream fout(filename.c_str(), ios::binary);
374
375 int header = 0;
376
377 fout.write((char*)&header,sizeof(int));
378 fout.write((char*)&resolutionX,sizeof(int));
379 fout.write((char*)&resolutionY,sizeof(int));
380 fout.write((char*)&resolutionZ,sizeof(int));
381
382 fout.write((char*)&(bmin_[0]),sizeof(double));
383 fout.write((char*)&(bmin_[1]),sizeof(double));
384 fout.write((char*)&(bmin_[2]),sizeof(double));
385
386 fout.write((char*)&(bmax_[0]),sizeof(double));
387 fout.write((char*)&(bmax_[1]),sizeof(double));
388 fout.write((char*)&(bmax_[2]),sizeof(double));
389
390 fout.write((char*)voronoiDiagram, sizeof(int) * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1));
391
392 fout.close();
393
394 return 0;
395 }
396
397 typedef struct
398 {
399 int i,j,k;
400 int di,dj,dk;
401 double fieldDist, gridDist, relError;
402 } errorData;
403
404 struct more_errorData : public std::binary_function< errorData, errorData, bool > {
operator ()more_errorData405 bool operator()(const errorData& x, const errorData& y) {
406 return((x.relError) > (y.relError));
407 }
408 };
409
sanityCheck()410 bool DistanceField::sanityCheck()
411 {
412 //double diagonal = sqrt(gridX*gridX + gridY*gridY + gridZ*gridZ);
413
414 bool result = true;
415 int count = 0;
416
417 const int numErrorsPrinted = 3;
418
419 vector<errorData> relErrors;
420 errorData emptyEntry = {0,0,0,0,0,0,0.0,0.0,-1.0};
421 for(int i=0; i<numErrorsPrinted; i++)
422 relErrors.push_back(emptyEntry);
423
424 for (int k=0; k <= resolutionZ; k++)
425 {
426 for (int j=0; j <= resolutionY; j++)
427 for (int i=0; i <= resolutionX; i++)
428 {
429 float d = distance(i,j,k);
430 float d1;
431 float sanity;
432 float relError;
433 float fieldDist;
434 float gridDist;
435
436 #define PROCESS(di,dj,dk)\
437 if ((i+(di) <= resolutionX) && (i+(di) >= 0) &&\
438 (j+(dj) <= resolutionY) && (j+(dj) >= 0) &&\
439 (k+(dk) <= resolutionZ) && (k+(dk) >= 0))\
440 {\
441 d1 = distance(i+(di),j+(dj),k+(dk));\
442 gridDist = (float) len(Vec3d((di)*gridX,(dj)*gridY,(dk)*gridZ));\
443 fieldDist = fabs(d-d1);\
444 sanity = fieldDist - gridDist;\
445 if (sanity > 1E-6)\
446 {\
447 relError = sanity/gridDist;\
448 if (relError > relErrors[numErrorsPrinted-1].relError)\
449 {\
450 errorData errorEntry = {i,j,k,di,dj,dk,fieldDist,gridDist,relError};\
451 relErrors[numErrorsPrinted-1] = errorEntry;\
452 sort(relErrors.begin(),relErrors.end(),more_errorData());\
453 }\
454 result = false;\
455 count++;\
456 }\
457 }
458
459 PROCESS(1,0,0);
460 PROCESS(1,1,0);
461 PROCESS(0,1,0);
462 PROCESS(-1,1,0);
463 PROCESS(-1,0,0);
464 PROCESS(-1,-1,0);
465 PROCESS(0,-1,0);
466 PROCESS(1,-1,0);
467
468 PROCESS(0,0,1);
469 PROCESS(1,0,1);
470 PROCESS(1,1,1);
471 PROCESS(0,1,1);
472 PROCESS(-1,1,1);
473 PROCESS(-1,0,1);
474 PROCESS(-1,-1,1);
475 PROCESS(0,-1,1);
476 PROCESS(1,-1,1);
477
478 PROCESS(0,0,-1);
479 PROCESS(1,0,-1);
480 PROCESS(1,1,-1);
481 PROCESS(0,1,-1);
482 PROCESS(-1,1,-1);
483 PROCESS(-1,0,-1);
484 PROCESS(-1,-1,-1);
485 PROCESS(0,-1,-1);
486 PROCESS(1,-1,-1);
487
488 }
489 cout << "." << flush;
490 }
491
492 cout << endl;
493
494 if (count == 0)
495 cout << "Success: sanity check passed." << endl;
496 else
497 {
498 cout << "Encountered " << count << " possible errors. Largest top " << numErrorsPrinted << " errors (or all errors if fewer):" << endl;
499 for(int i=0; i< (count < numErrorsPrinted ? count : numErrorsPrinted); i++)
500 {
501 errorData * errorEntry = &relErrors[i];
502 float d1 = distance(errorEntry->i,errorEntry->j,errorEntry->k);
503 float d2 = distance(errorEntry->i + errorEntry->di ,errorEntry->j + errorEntry->dj, errorEntry->k + errorEntry->dk);
504 cout << "Distance field change too large. [" << errorEntry->i << "," << errorEntry->j << "," << errorEntry->k << "] to [" << errorEntry->i + (errorEntry->di) << "," << errorEntry->j + (errorEntry->dj) << "," << errorEntry->k + (errorEntry->dk) << "]" << " Dist 1: " << d1 << " Dist 2: " << d2 << " Reported change in distance field: " << errorEntry->fieldDist << " Grid distance: " << errorEntry->gridDist << " Relative error: " << errorEntry->relError << endl;
505 }
506 }
507 return result;
508 }
509
distance(Vec3d pos,int constrainToBox)510 float DistanceField::distance(Vec3d pos, int constrainToBox)
511 {
512 // get the index coordinate of the lower-right-bottom corner of the voxel containing 'pos'
513 int i = (int)((pos[0] - bmin_[0]) * invGridX);
514 int j = (int)((pos[1] - bmin_[1]) * invGridY);
515 int k = (int)((pos[2] - bmin_[2]) * invGridZ);
516
517 if (((i<0) || (i>=resolutionX) || (j<0) || (j>=resolutionY) || (k<0) || (k>=resolutionZ)) && (!constrainToBox))
518 {
519 printf("Warning: querying the distance field outside of the bounding box: (i,j,k)=(%d,%d,%d), (x, y, z)=(%lf,%lf,%lf), resolution=(%d,%d,%d)\n",i, j, k, pos[0], pos[1], pos[2], resolutionX, resolutionY, resolutionZ);
520 return FLT_MAX;
521 }
522
523 if (constrainToBox)
524 {
525 if (i >= resolutionX)
526 {
527 i = resolutionX - 1;
528 pos[0] = bmax_[0];
529 }
530
531 if (i < 0)
532 {
533 i = 0;
534 pos[0] = bmin_[0];
535 }
536
537 if (j >= resolutionY)
538 {
539 j = resolutionY - 1;
540 pos[1] = bmax_[1];
541 }
542
543 if (j < 0)
544 {
545 j = 0;
546 pos[1] = bmin_[1];
547 }
548
549 if (k >= resolutionZ)
550 {
551 k = resolutionZ - 1;
552 pos[2] = bmax_[2];
553 }
554
555 if (k < 0)
556 {
557 k = 0;
558 pos[2] = bmin_[2];
559 }
560 }
561
562 //printf("I=%d J=%d K=%d\n", i,j,k);
563 //printf("remx=%f remy=%f remz=%f\n", pos[0] - bmin_[0] - gridX * i,
564 // pos[1] - bmin_[1] - gridY * j, pos[2] - bmin_[2] - gridZ * k);
565
566 double wx,wy,wz;
567 wx = ((pos[0]-bmin_[0]) / gridX) - i;
568 wy = ((pos[1]-bmin_[1]) / gridY) - j;
569 wz = ((pos[2]-bmin_[2]) / gridZ) - k;
570
571 return (float) (TRILINEAR_INTERPOLATION(wx,wy,wz,distance(i,j,k),distance(i+1,j,k),distance(i+1,j+1,k),distance(i,j+1,k),
572 distance(i,j,k+1),distance(i+1,j,k+1),distance(i+1,j+1,k+1),distance(i,j+1,k+1)));
573 }
574
maxValue()575 float DistanceField::maxValue()
576 {
577 float maxValue=-FLT_MAX;
578 for (int k=0; k <= resolutionZ; k++)
579 for (int j=0; j <= resolutionY; j++)
580 for (int i=0; i <= resolutionX; i++)
581 {
582 float dist = distance(i,j,k);
583 if (dist > maxValue)
584 maxValue = dist;
585 }
586 return maxValue;
587 }
588
minValue()589 float DistanceField::minValue()
590 {
591 float minValue=FLT_MAX;
592 for (int k=0; k <= resolutionZ; k++)
593 for (int j=0; j <= resolutionY; j++)
594 for (int i=0; i <= resolutionX; i++)
595 {
596 float dist = distance(i,j,k);
597 if (dist < minValue)
598 minValue = dist;
599 }
600 return minValue;
601 }
602
maxMinValue(float * maxValue,float * minValue)603 void DistanceField::maxMinValue(float* maxValue, float* minValue)
604 {
605 *minValue=FLT_MAX;
606 *maxValue=-FLT_MAX;
607 for (int k=0; k <= resolutionZ; k++)
608 for (int j=0; j <= resolutionY; j++)
609 for (int i=0; i <= resolutionX; i++)
610 {
611 float dist = distance(i,j,k);
612 if (dist < *minValue)
613 *minValue = dist;
614 if (dist > *maxValue)
615 *maxValue = dist;
616 }
617 }
618
maxAbsValue()619 float DistanceField::maxAbsValue()
620 {
621 float maxValue=0;
622 for (int k=0; k <= resolutionZ; k++)
623 for (int j=0; j <= resolutionY; j++)
624 for (int i=0; i <= resolutionX; i++)
625 {
626 float dist = fabs(distance(i,j,k));
627 if (dist > maxValue)
628 maxValue = dist;
629 }
630 return maxValue;
631 }
632
maxNonInftyAbsValue()633 float DistanceField::maxNonInftyAbsValue()
634 {
635 float maxValue=0;
636 for (int k=0; k <= resolutionZ; k++)
637 for (int j=0; j <= resolutionY; j++)
638 for (int i=0; i <= resolutionX; i++)
639 {
640 float dist = fabs(distance(i,j,k));
641 if ((dist > maxValue) && (dist != FLT_MAX))
642 maxValue = dist;
643 }
644 return maxValue;
645 }
646
maxAbsValue(float threshold)647 float DistanceField::maxAbsValue(float threshold)
648 {
649 float maxValue=0;
650 for (int k=0; k <= resolutionZ; k++)
651 for (int j=0; j <= resolutionY; j++)
652 for (int i=0; i <= resolutionX; i++)
653 {
654 float dist = fabs(distance(i,j,k));
655 if ((dist > maxValue) && (dist < threshold))
656 maxValue = dist;
657 }
658 return maxValue;
659 }
660
gradient(const Vec3d & pos)661 Vec3d DistanceField::gradient(const Vec3d& pos)
662 {
663 int i,j,k;
664
665 // get the indices
666 i = (int)((pos[0] - bmin_[0]) * invGridX);
667 j = (int)((pos[1] - bmin_[1]) * invGridY);
668 k = (int)((pos[2] - bmin_[2]) * invGridZ);
669
670 if ((i<=0) || (i>=resolutionX) || (j<=0) || (j>=resolutionY) || (k<=0) || (k>=resolutionZ))
671 {
672 return Vec3d(0,0,0);
673 }
674
675 double wx,wy,wz;
676 wx = ((pos[0]-bmin_[0]) / gridX) - i;
677 wy = ((pos[1]-bmin_[1]) / gridY) - j;
678 wz = ((pos[2]-bmin_[2]) / gridZ) - k;
679
680 // gradient with respect to trilinear interpolation
681
682 float v000 = distance(i,j,k);
683 float v100 = distance(i+1,j,k);
684 float v110 = distance(i+1,j+1,k);
685 float v010 = distance(i,j+1,k);
686 float v001 = distance(i,j,k+1);
687 float v101 = distance(i+1,j,k+1);
688 float v111 = distance(i+1,j+1,k+1);
689 float v011 = distance(i,j+1,k+1);
690
691 return Vec3d(
692 GRADIENT_COMPONENT_X(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011),
693 GRADIENT_COMPONENT_Y(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011),
694 GRADIENT_COMPONENT_Z(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) );
695
696 }
697
getDistanceData(float ** floatBuffer)698 void DistanceField::getDistanceData(float ** floatBuffer)
699 {
700 unsigned int size = (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
701 *floatBuffer = (float*) malloc (sizeof(float) * size);
702 memcpy(*floatBuffer, distanceData, sizeof(float) * size);
703 //for(unsigned int i=0; i< size; i++)
704 //(*floatBuffer)[i] = (float) (distanceData[i]);
705 }
706
707
708 /*
709 void DistanceField::convertToFloat(float ** floatBuffer)
710 {
711 unsigned int size = (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
712 *floatBuffer = (float*) malloc (sizeof(float) * size);
713 for(unsigned int i=0; i< size; i++)
714 (*floatBuffer)[i] = (float) (distanceData[i]);
715 }
716 */
717
718 /*
719 void DistanceField::convertToDouble(double ** doubleBuffer)
720 {
721 unsigned int size = (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
722 *doubleBuffer = (double*) malloc (sizeof(double) * size);
723 memcpy(*doubleBuffer, distanceData, sizeof(double) * size);
724 }
725 */
726
isSurfaceVoxel(int i,int j,int k)727 bool DistanceField::isSurfaceVoxel(int i, int j, int k)
728 {
729 float v[8];
730 v[0] = distance(i,j,k);
731 v[1] = distance(i+1,j,k);
732 v[2] = distance(i+1,j+1,k);
733 v[3] = distance(i,j+1,k);
734 v[4] = distance(i,j,k+1);
735 v[5] = distance(i+1,j,k+1);
736 v[6] = distance(i+1,j+1,k+1);
737 v[7] = distance(i,j+1,k+1);
738
739 bool allPositive = true;
740 for(int l=0; l<8; l++)
741 if (v[l] < 0)
742 {
743 allPositive = false;
744 break;
745 }
746
747 bool allNegative = true;
748 for(int l=0; l<8; l++)
749 if (v[l] >= 0)
750 {
751 allNegative = false;
752 break;
753 }
754
755 return (!allNegative && !allPositive);
756 }
757
isSurfaceVoxel(int customResolutionX,int customResolutionY,int customResolutionZ,int i,int j,int k,float levelSetValue)758 bool DistanceField::isSurfaceVoxel(int customResolutionX, int customResolutionY, int customResolutionZ, int i, int j, int k, float levelSetValue)
759 {
760 //printf("i: %d j:%d k:%d\n",i,j,k);
761 Vec3d customGrid;
762 customGrid[0] = 1.0 * side[0] / customResolutionX;
763 customGrid[1] = 1.0 * side[1] / customResolutionY;
764 customGrid[2] = 1.0 * side[2] / customResolutionZ;
765
766 Vec3d basePos = bmin_ + Vec3d( 1.0 * i * customGrid[0], 1.0 * j * customGrid[1], 1.0 * k * customGrid[2] );
767
768 if (((i < 0) || (i >= customResolutionX)) && ((j < 0) || (j >= customResolutionY)) && ((k < 0) || (k >= customResolutionZ)))
769 {
770 printf("Warning (inside isSurfaceVoxel): voxel insides specified outside of the bounding box: (i,j,k)=(%d,%d,%d), customResolution=(%d,%d,%d)\n", i, j, k, customResolutionX, customResolutionY, customResolutionZ);
771 }
772
773 // pass parameter 1 to constrain to box
774 float v[8];
775 v[0] = distance(basePos, 1) - levelSetValue;
776 v[1] = distance(basePos + Vec3d(customGrid[0], 0, 0), 1) - levelSetValue;
777 v[2] = distance(basePos + Vec3d(customGrid[0], customGrid[1], 0), 1) - levelSetValue;
778 v[3] = distance(basePos + Vec3d(0, customGrid[1], 0), 1) - levelSetValue;
779 v[4] = distance(basePos + Vec3d(0, 0, customGrid[2]), 1) - levelSetValue;
780 v[5] = distance(basePos + Vec3d(customGrid[0], 0, customGrid[2]), 1) - levelSetValue;
781 v[6] = distance(basePos + Vec3d(customGrid[0], customGrid[1], customGrid[2]), 1) - levelSetValue;
782 v[7] = distance(basePos + Vec3d(0, customGrid[1], customGrid[2]), 1) - levelSetValue;
783
784 /*
785 double v[8];
786 v[0] = distance(i,j,k);
787 v[1] = distance(i+1,j,k);
788 v[2] = distance(i+1,j+1,k);
789 v[3] = distance(i,j+1,k);
790 v[4] = distance(i,j,k+1);
791 v[5] = distance(i+1,j,k+1);
792 v[6] = distance(i+1,j+1,k+1);
793 v[7] = distance(i,j+1,k+1);
794 */
795
796 bool allPositive = true;
797 for(int l=0; l<8; l++)
798 if (v[l] < 0)
799 {
800 allPositive = false;
801 break;
802 }
803
804 bool allNegative = true;
805 for(int l=0; l<8; l++)
806 if (v[l] >= 0)
807 {
808 allNegative = false;
809 break;
810 }
811
812 return (!allNegative && !allPositive);
813 }
814
numSurfaceVoxels(float levelSetValue)815 int DistanceField::numSurfaceVoxels(float levelSetValue)
816 {
817 return numSurfaceVoxels(resolutionX, resolutionY, resolutionZ, levelSetValue);
818 }
819
numSurfaceVoxels(int customResolutionX,int customResolutionY,int customResolutionZ,float levelSetValue)820 int DistanceField::numSurfaceVoxels(int customResolutionX, int customResolutionY, int customResolutionZ, float levelSetValue)
821 {
822 printf("num surface voxels... custom res: (%d,%d,%d)\n", customResolutionX, customResolutionY, customResolutionZ);
823 int count = 0;
824
825 for(int k=0; k<customResolutionZ; k++)
826 for(int j=0; j<customResolutionY; j++)
827 for(int i=0; i<customResolutionX; i++)
828 if (isSurfaceVoxel(customResolutionX, customResolutionY, customResolutionZ, i, j, k, levelSetValue))
829 count++;
830
831 return count;
832 }
833
minBoundaryValue()834 float DistanceField::minBoundaryValue()
835 {
836 float minDistance = FLT_MAX;
837 for(int k=0; k <= resolutionZ; k++)
838 for(int j=0; j <= resolutionY; j++)
839 for(int i=0; i <= resolutionX; i += resolutionX)
840 {
841 float value = distance(i,j,k);
842 if (value < minDistance)
843 minDistance = value;
844 }
845
846 for(int k=0; k <= resolutionZ; k++)
847 for(int j=0; j <= resolutionY; j += resolutionY)
848 for(int i=0; i <= resolutionX; i++)
849 {
850 float value = distance(i,j,k);
851 if (value < minDistance)
852 minDistance = value;
853 }
854
855 for(int k=0; k <= resolutionZ; k += resolutionZ)
856 for(int j=0; j <= resolutionY; j++)
857 for(int i=0; i <= resolutionX; i++)
858 {
859 float value = distance(i,j,k);
860 if (value < minDistance)
861 minDistance = value;
862 }
863
864 minBoundaryDistance = minDistance;
865 return minDistance;
866 }
867
print()868 void DistanceField::print()
869 {
870 for(int k=0; k <= resolutionX; k++)
871 for(int j=0; j <= resolutionY; j++)
872 for(int i=0; i <= resolutionZ; i++)
873 {
874 printf("i=%d j=%d k=%d d=%G\n", i, j, k, distance(i,j,k));
875 }
876 }
877
computeFloodFillTag(ObjMesh * objMeshIn)878 void DistanceField::computeFloodFillTag(ObjMesh* objMeshIn)
879 {
880 ObjMesh objMesh(*objMeshIn);
881 vegalong size = sizeof(char) * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
882 if (floodFillTag != NULL)
883 free(floodFillTag);
884 floodFillTag = (char*) malloc(size);
885 memset(floodFillTag, 0, sizeof(char) * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1));
886
887 typedef triple<int,int,int> voxel;
888
889 std::set<voxel> checkedVoxels;
890 std::vector<voxel> scheduledVoxels;
891
892 for(unsigned int i=0; i < objMesh.getNumGroups(); ++i)
893 {
894 const ObjMesh::Group * group = objMesh.getGroupHandle(i);
895
896 for (unsigned int j=0; j<group->getNumFaces(); ++j)
897 {
898 Vec3d p0 = objMesh.getPosition(group->getFace(j).getVertex(0).getPositionIndex());
899 Vec3d p1 = objMesh.getPosition(group->getFace(j).getVertex(1).getPositionIndex());
900 Vec3d p2 = objMesh.getPosition(group->getFace(j).getVertex(2).getPositionIndex());
901
902 TriangleBasic triangle(p0,p1,p2);
903
904 Vec3d center = 1.0/3 * (p0 + p1 + p2);
905 Vec3d recCenter = center - bmin_;
906
907 int vi,vj,vk;
908
909 vi = (int)(recCenter[0] / gridX);
910 vj = (int)(recCenter[1] / gridY);
911 vk = (int)(recCenter[2] / gridZ);
912
913 checkedVoxels.clear();
914 checkedVoxels.insert(voxel(vi, vj, vk));
915
916 scheduledVoxels.clear();
917 scheduledVoxels.push_back(voxel(vi,vj,vk));
918
919 while (!scheduledVoxels.empty())
920 {
921 voxel v = scheduledVoxels.back();
922 scheduledVoxels.pop_back();
923
924 Vec3d bbmin = bmin_ + Vec3d(v.first * gridX, v.second * gridY, v.third * gridZ);
925 Vec3d bbmax = bbmin + Vec3d(gridX, gridY, gridZ);
926
927 BoundingBox bbox(bbmin, bbmax);
928
929 if (triangle.doesIntersectBox(bbox))
930 {
931 //surfaceVoxels.insert(v);
932 //floodFillTag[(v.third * (resolutionY+1) + v.second) * (resolutionX + 1) + v.first] = 1;
933
934 voxel neighbor;
935 vegalong index;
936 #define TAGNEIGHBOR(ii,jj,kk)\
937 neighbor = voxel(v.first+(ii), v.second+(jj), v.third+(kk));\
938 if ((neighbor.first <= resolutionX) &&\
939 (neighbor.second <= resolutionY) &&\
940 (neighbor.third <= resolutionZ))\
941 {\
942 index = (neighbor.third * (resolutionY+1) + neighbor.second) * (resolutionX + 1) + neighbor.first;\
943 floodFillTag[index] = 1;\
944 }
945
946 for (int kkk=0; kkk<=1; ++kkk)
947 for (int jjj=0; jjj<=1; ++jjj)
948 for (int iii=0; iii<=1; ++iii)
949 {
950 TAGNEIGHBOR(iii, jjj, kkk);
951 }
952
953
954 #define CHECKNEIGHBOR(ii,jj,kk)\
955 neighbor = voxel(v.first+(ii), v.second+(jj), v.third+(kk));\
956 if ((neighbor.first >= 0) && (neighbor.first <= resolutionX) &&\
957 (neighbor.second >= 0) && (neighbor.second <= resolutionY) &&\
958 (neighbor.third >= 0) && (neighbor.third <= resolutionZ))\
959 {\
960 if (checkedVoxels.find(neighbor) == checkedVoxels.end())\
961 {\
962 checkedVoxels.insert(neighbor);\
963 scheduledVoxels.push_back(neighbor);\
964 }\
965 }
966
967 for (int kkk=-1; kkk<=1; ++kkk)
968 for (int jjj=-1; jjj<=1; ++jjj)
969 for (int iii=-1; iii<=1; ++iii)
970 {
971 if ((iii == 0) && (jjj == 0) && (kkk == 0))
972 continue;
973
974 CHECKNEIGHBOR(iii, jjj, kkk);
975 }
976 }
977 }
978 }
979 }
980 }
981
pointCCD(Vec3d startPos,Vec3d endPos)982 double DistanceField::pointCCD(Vec3d startPos, Vec3d endPos)
983 {
984 int constrainToBox = 0;
985 double startDist = distance(startPos, constrainToBox);
986 double endDist = distance(endPos, constrainToBox);
987
988 if (((startDist > 0) && (endDist > 0)) || (startDist - endDist == 0))
989 return -1.0;
990
991 int flip = 0;
992 if (startDist > 0)
993 {
994 flip = 1;
995 Vec3d bufv = startPos;
996 startPos = endPos;
997 endPos = bufv;
998 double buf = startDist;
999 startDist = endDist;
1000 endDist = buf;
1001 }
1002
1003 // now, startPos is inside, and endPos is outside
1004
1005 double eps = 1E-12;
1006 double d;
1007 Vec3d x;
1008 double t = 0.0;
1009 double tLo = 0.0;
1010 double tHi = 1.0;
1011 Vec3d xLo = startPos;
1012 Vec3d xHi = endPos;
1013 double distLo = startDist;
1014 double distHi = endDist;
1015 do
1016 {
1017 // distLo + deltaT * (distHi - distLo) = 0
1018 t += distLo / (distLo - distHi) * (tHi - tLo);
1019 x = startPos + t * (endPos - startPos);
1020 d = distance(x, constrainToBox);
1021
1022 if (d <= 0)
1023 {
1024 // lo must go to new point
1025 tLo = t;
1026 xLo = x;
1027 distLo = d;
1028 }
1029 else
1030 {
1031 // hi must go to new point
1032 tHi = t;
1033 xHi = x;
1034 distHi = d;
1035 }
1036 }
1037 while (fabs(d) > eps);
1038
1039 if (flip)
1040 t = 1.0 - t;
1041
1042 return t;
1043 }
1044
offsetDistanceField(double offset)1045 void DistanceField::offsetDistanceField(double offset)
1046 {
1047 vegalong numGridPoints = (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
1048
1049 printf("Applying offset %G to the distance field. Resolution is %d x %d x %d.\n", offset, resolutionX, resolutionY, resolutionZ);
1050
1051 for(vegalong i=0; i<numGridPoints; i++)
1052 distanceData[i] += (float) offset;
1053 }
1054
1055