Cleaver Tetrahedral Meshing  2.2.1
Cleaving algorithm for high quality tetrahedral meshing
All Classes Pages
main.cpp
1 
2 
3 /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
4 /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
5 //
6 // Cleaver - A MultiMaterial Conforming Tetrahedral Meshing Library
7 // - Command Line Program
8 //
9 // Primary Author: Jonathan Bronson (bronson@sci.utah.edu)
10 //
11 /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
12 /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
13 //
14 //-------------------------------------------------------------------
15 //
16 // Copyright (C) 2014, Jonathan Bronson
17 // Scientific Computing & Imaging Institute
18 // University of Utah
19 //
20 // Permission is hereby granted, free of charge, to any person
21 // obtaining a copy of this software and associated documentation
22 // files ( the "Software" ), to deal in the Software without
23 // restriction, including without limitation the rights to use,
24 // copy, modify, merge, publish, distribute, sublicense, and/or
25 // sell copies of the Software, and to permit persons to whom the
26 // Software is furnished to do so, subject to the following
27 // conditions:
28 //
29 // The above copyright notice and this permission notice shall
30 // be included in all copies or substantial portions of the
31 // Software.
32 //
33 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
34 // KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
35 // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
36 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
37 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
39 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
40 // USE OR OTHER DEALINGS IN THE SOFTWARE.
41 //-------------------------------------------------------------------
42 //
43 /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
44 #include <Cleaver/Cleaver.h>
45 #include <Cleaver/CleaverMesher.h>
46 #include <Cleaver/InverseField.h>
47 #include <Cleaver/SizingFieldCreator.h>
48 #include <Cleaver/Timer.h>
49 #include <NRRDTools.h>
50 
51 #include <boost/program_options.hpp>
52 
53 // STL Includes
54 #include <exception>
55 #include <iostream>
56 #include <vector>
57 #include <sstream>
58 #include <fstream>
59 #include <string>
60 #include <ctime>
61 
62 #ifdef WIN32
63 #define NOMINMAX
64 #endif
65 
66 const std::string scirun = "scirun";
67 const std::string tetgen = "tetgen";
68 const std::string matlab = "matlab";
69 const std::string vtkPoly = "vtkPoly";
70 const std::string vtkUSG = "vtkUSG";
71 const std::string ply = "ply";
72 
73 const std::string kDefaultOutputPath = "./";
74 const std::string kDefaultOutputName = "output";
75 const cleaver::MeshFormat kDefaultOutputFormat = cleaver::Tetgen;
76 
77 
78 const double kDefaultAlpha = 0.4;
79 const double kDefaultAlphaLong = 0.357;
80 const double kDefaultAlphaShort = 0.203;
81 const double kDefaultScale = 1.0;
82 const double kDefaultLipschitz = 0.2;
83 const double kDefaultMultiplier = 1.0;
84 const int kDefaultPadding = 0;
85 const int kDefaultMaxIterations = 1000;
86 const double kDefaultSigma = 1.;
87 
88 namespace po = boost::program_options;
89 
90 // Entry Point
91 int main(int argc, char* argv[])
92 {
93  bool verbose = false;
94  bool fix_tets = false;
95  bool segmentation = false;
96  std::vector<std::string> material_fields;
97  std::string sizing_field;
98  std::string background_mesh;
99  std::string output_path = kDefaultOutputPath;
100  std::string output_name = kDefaultOutputName;
101  double alpha = kDefaultAlpha;
102  double alpha_long = kDefaultAlphaLong;
103  double alpha_short = kDefaultAlphaShort;
104  double lipschitz = kDefaultLipschitz;
105  double multiplier = kDefaultMultiplier;
106  double scaling = kDefaultScale;
107  int padding = kDefaultPadding;
108  bool have_sizing_field = false;
109  bool have_background_mesh = false;
110  bool write_background_mesh = false;
111  bool strict = false;
112  bool strip_exterior = false;
113  enum cleaver::MeshType mesh_mode = cleaver::Structured;
114  cleaver::MeshFormat output_format = kDefaultOutputFormat;
115  double sigma = kDefaultSigma;
116 
117  double sizing_field_time = 0;
118  double background_time = 0;
119  double cleaving_time = 0;
120 
121 
122  //-------------------------------
123  // Parse Command Line Params
124  //-------------------------------
125  try {
126  po::options_description description("Command line flags");
127  description.add_options()
128  ("help,h", "display help message")
129  ("verbose,v", "enable verbose output")
130  ("version,V", "display version information")
131  ("input_files,i", po::value<std::vector<std::string> >()->multitoken(), "material field paths or segmentation path")
132  ("background_mesh,b", po::value<std::string>(), "input background mesh")
133  ("mesh_mode,m", po::value<std::string>(), "background mesh mode (adaptive [default], constant)")
134  ("alpha,a", po::value<double>(), "initial alpha value")
135  ("alpha_short,s", po::value<double>(), "alpha short value for regular mesh_mode")
136  ("alpha_long,l", po::value<double>(), "alpha long value for regular mesh_mode")
137  ("sizing_field,z", po::value<std::string>(), "sizing field path")
138  ("grading,g", po::value<double>(), "sizing field grading")
139  ("scale,c", po::value<double>(), "sizing field scale factor")
140  ("multiplier,x", po::value<double>(), "sizing field multiplier")
141  ("padding,p", po::value<int>(), "volume padding")
142  ("write_background_mesh,w", "write background mesh")
143  ("fix_tet_windup,j", "Ensure positive Jacobians with proper vertex wind-up.")
144  ("strip_exterior,e", "strip exterior tetrahedra")
145  ("output_path,o", po::value<std::string>(), "output path prefix")
146  ("output_name,n", po::value<std::string>(), "output mesh name [default 'output']")
147  ("output_format,f", po::value<std::string>(), "output mesh format (tetgen [default], scirun, matlab, vtkUSG, vtkPoly, ply [Surface mesh only])")
148  ("strict,t", "warnings become errors")
149  ("segmentation,S", "The input file is a segmentation file.")
150  ("blend_sigma,B", po::value<double>(), "blending sigma for input(s) to remove alias artifacts.")
151  ;
152 
153  boost::program_options::variables_map variables_map;
154  boost::program_options::store(boost::program_options::parse_command_line(argc, argv, description), variables_map);
155  boost::program_options::notify(variables_map);
156 
157  // print help
158  if (variables_map.count("help") || (argc == 1)) {
159  std::cout << description << std::endl;
160  return 0;
161  }
162 
163  // print version info
164  if (variables_map.count("version")) {
165  std::cout << cleaver::Version << std::endl;
166  return 0;
167  }
168 
169  // enable verbose mode
170  if (variables_map.count("verbose")) {
171  verbose = true;
172  }
173 
174  // enable segmentation
175  if (variables_map.count("segmentation")) {
176  segmentation = true;
177  }
178  if (variables_map.count("strict")) {
179  strict = true;
180  }
181 
182  // parse the material field input file names
183  if (variables_map.count("input_files")) {
184  material_fields = variables_map["input_files"].as<std::vector<std::string> >();
185  } else {
186  std::cerr << "Error: At least one material field file must be specified." << std::endl;
187  return 1;
188  }
189 
190  //-----------------------------------------
191  // parse the sizing field input file name
192  // and check for conflicting parameters
193  //----------------------------------------
194  if (variables_map.count("sizing_field")) {
195  have_sizing_field = true;
196  sizing_field = variables_map["sizing_field"].as<std::string>();
197 
198  if (variables_map.count("grading")) {
199  if (!strict)
200  std::cerr << "Warning: sizing field provided, grading will be ignored." << std::endl;
201  else {
202  std::cerr << "Error: both sizing field and grading parameter provided." << std::endl;
203  return 2;
204  }
205  }
206  if (variables_map.count("multiplier")) {
207  if (!strict)
208  std::cerr << "Warning: sizing field provided, multiplier will be ignored." << std::endl;
209  else {
210  std::cerr << "Error: both sizing field and multiplier parameter provided." << std::endl;
211  return 3;
212  }
213  }
214  if (variables_map.count("scale")) {
215  if (!strict)
216  std::cerr << "Warning: sizing field provided, scale will be ignored." << std::endl;
217  else {
218  std::cerr << "Error: both sizing field and scale parameter provided." << std::endl;
219  return 4;
220  }
221  }
222  }
223 
224  // parse sizing field parameters
225  if (variables_map.count("grading")) {
226  lipschitz = variables_map["grading"].as<double>();
227  }
228  if (variables_map.count("scaling")) {
229  scaling = variables_map["scaling"].as<double>();
230  }
231  if (variables_map.count("multiplier")) {
232  multiplier = variables_map["multiplier"].as<double>();
233  }
234  if (variables_map.count("padding")) {
235  padding = variables_map["padding"].as<int>();
236  }
237  fix_tets = variables_map.count("fix_tet_windup") == 0 ? false : true;
238 
239  if (variables_map.count("alpha")) {
240  alpha = variables_map["alpha"].as<double>();
241  }
242  if (variables_map.count("alpha_short")) {
243  alpha_short = variables_map["alpha_short"].as<double>();
244  }
245  if (variables_map.count("alpha_long")) {
246  alpha_long = variables_map["alpha_long"].as<double>();
247  }
248  if (variables_map.count("blend_sigma")) {
249  sigma = variables_map["blend_sigma"].as<double>();
250  }
251 
252  if (variables_map.count("background_mesh")) {
253  have_background_mesh = true;
254  background_mesh = variables_map["background_mesh"].as<std::string>();
255 
256  if (variables_map.count("sizing_field")) {
257  if (!strict)
258  std::cerr << "Warning: background mesh provided, sizing field will be ignored." << std::endl;
259  else {
260  std::cerr << "Error: both background mesh and sizing field provided." << std::endl;
261  return 5;
262  }
263  }
264  }
265 
266 
267  // parse the background mesh mode
268  if (variables_map.count("mesh_mode")) {
269  std::string mesh_mode_string = variables_map["mesh_mode"].as<std::string>();
270  if (mesh_mode_string.compare("adaptive") == 0) {
271  mesh_mode = cleaver::Regular;
272  } else if (mesh_mode_string.compare("constant") == 0) {
273  mesh_mode = cleaver::Structured;
274  } else {
275  std::cerr << "Error: invalid background mesh mode: " << mesh_mode_string << std::endl;
276  std::cerr << "Valid Modes: [regular] [structured] " << std::endl;
277  return 6;
278  }
279  }
280 
281  // does user want background mesh improvement?
282  //if (variables_map.count("mesh_improve")) {
283  // improve_mesh = true;
284  //}
285 
286  // strip exterior tetra after mesh generation
287  if (variables_map.count("strip_exterior")) {
288  strip_exterior = true;
289  }
290 
291  // enable writing background mesh if request
292  if (variables_map.count("write_background_mesh")) {
293  write_background_mesh = true;
294  }
295 
296  // set the proper output mesh format
297  if (variables_map.count("output_format")) {
298  std::string format_string = variables_map["output_format"].as<std::string>();
299  if (format_string.compare(tetgen) == 0) {
300  output_format = cleaver::Tetgen;
301  } else if (format_string.compare(scirun) == 0) {
302  output_format = cleaver::Scirun;
303  } else if (format_string.compare(matlab) == 0) {
304  output_format = cleaver::Matlab;
305  } else if (format_string.compare(vtkPoly) == 0) {
306  output_format = cleaver::VtkPoly;
307  } else if (format_string.compare(vtkUSG) == 0) {
308  output_format = cleaver::VtkUSG;
309  } else if (format_string.compare(ply) == 0) {
310  output_format = cleaver::PLY;
311  } else {
312  std::cerr << "Error: unsupported output format: " << format_string << std::endl;
313  return 7;
314  }
315  }
316 
317  // set output path
318  if (variables_map.count("output_path")) {
319  output_path = variables_map["output_path"].as<std::string>();
320  }
321 
322  // set output mesh name
323  if (variables_map.count("output_name")) {
324  output_name = variables_map["output_name"].as<std::string>();
325  }
326 
327  } catch (std::exception& e) {
328  std::cerr << "Error: " << e.what() << std::endl;
329  return 8;
330  } catch (...) {
331  std::cerr << "Unhandled exception caught. Terminating." << std::endl;
332  return 9;
333  }
334 
335  //-----------------------------------
336  // Load Data & Construct Volume
337  //-----------------------------------
338  cleaver::Timer total_timer;
339  total_timer.start();
340  bool add_inverse = false;
341  std::vector<cleaver::AbstractScalarField*> fields;
342  if (material_fields.empty()) {
343  std::cerr << "No material fields or segmentation files provided. Terminating."
344  << std::endl;
345  return 10;
346  }
347  if (segmentation && material_fields.size() == 1) {
348  NRRDTools::segmentationToIndicatorFunctions(material_fields[0], sigma);
349  } else {
350  if (material_fields.size() > 1) {
351  std::cerr << "WARNING: More than 1 input provided for segmentation." <<
352  " This will be assumed to be indicator functions." << std::endl;
353  }
354  if (material_fields.size() == 1) {
355  add_inverse = true;
356  }
357  if (verbose) {
358  std::cout << " Loading input fields:" << std::endl;
359  for (size_t i = 0; i < material_fields.size(); i++) {
360  std::cout << " - " << material_fields[i] << std::endl;
361  }
362  }
363  fields = NRRDTools::loadNRRDFiles(material_fields);
364  if (fields.empty()) {
365  std::cerr << "Failed to load image data. Terminating." << std::endl;
366  return 10;
367  } else if (add_inverse)
368  fields.push_back(new cleaver::InverseScalarField(fields[0]));
369  }
370  cleaver::Volume *volume = new cleaver::Volume(fields);
371  cleaver::CleaverMesher mesher;
372  mesher.setVolume(volume);
373  mesher.setAlphaInit(alpha);
374 
375  //-----------------------------------
376  // Load background mesh if provided
377  //-----------------------------------
378  cleaver::TetMesh *bgMesh = nullptr;
379  if (have_background_mesh) {
380  std::string nodeFileName = background_mesh + ".node";
381  std::string eleFileName = background_mesh + ".ele";
382  if (verbose) {
383  std::cout << "Loading background mesh: \n\t" << nodeFileName
384  << "\n\t" << eleFileName << std::endl;
385  }
386  bgMesh = cleaver::TetMesh::createFromNodeElePair(nodeFileName, eleFileName);
387  mesher.setBackgroundMesh(bgMesh);
388  }
389  //-----------------------------------
390  // otherwise take steps to compute one
391  //-----------------------------------
392  else {
393 
394  //------------------------------------------------------------
395  // Load or Construct Sizing Field
396  //------------------------------------------------------------
397  std::vector<cleaver::AbstractScalarField *> sizingField;
398  if (have_sizing_field) {
399  std::cout << "Loading sizing field: " << sizing_field << std::endl;
400  std::vector<std::string> tmp(1,sizing_field);
401  sizingField = NRRDTools::loadNRRDFiles(tmp);
402  // todo(jon): add error handling
403  } else {
404  cleaver::Timer sizing_field_timer;
405  sizing_field_timer.start();
406  sizingField.push_back(cleaver::SizingFieldCreator::createSizingFieldFromVolume(
407  volume,
408  (float)(1.0 / lipschitz),
409  (float)scaling,
410  (float)multiplier,
411  (int)padding,
412  (mesh_mode == cleaver::Regular ? false : true),
413  verbose));
414  sizing_field_timer.stop();
415  sizing_field_time = sizing_field_timer.time();
416  }
417 
418  //------------------------------------------------------------
419  // Set Sizing Field on Volume
420  //------------------------------------------------------------
421  volume->setSizingField(sizingField[0]);
422 
423 
424  //-----------------------------------------------------------
425  // Construct Background Mesh
426  //-----------------------------------------------------------
427  cleaver::Timer background_timer;
428  background_timer.start();
429  if (verbose)
430  std::cout << "Creating Octree Mesh..." << std::endl;
431  switch (mesh_mode) {
432 
433  case cleaver::Regular:
434  mesher.setAlphas(alpha_long, alpha_short);
435  mesher.setRegular(true);
436  bgMesh = mesher.createBackgroundMesh(verbose);
437  break;
438  default:
439  case cleaver::Structured:
440  mesher.setRegular(false);
441  bgMesh = mesher.createBackgroundMesh(verbose);
442  break;
443  }
444  background_timer.stop();
445  background_time = background_timer.time();
446  mesher.setBackgroundTime(background_time);
447  }
448 
449 
450  //-----------------------------------------------------------
451  // Write Background Mesh if requested
452  //-----------------------------------------------------------
453  if (bgMesh && write_background_mesh) {
454  bgMesh->writeNodeEle(output_path + "bgmesh", false, false, false);
455  }
456 
457  //-----------------------------------------------------------
458  // Apply Mesh Cleaving
459  //-----------------------------------------------------------
460  cleaver::Timer cleaving_timer;
461  cleaving_timer.start();
462  mesher.buildAdjacency(verbose);
463  mesher.sampleVolume(verbose);
464  mesher.computeAlphas(verbose);
465  mesher.computeInterfaces(verbose);
466  mesher.generalizeTets(verbose);
467  mesher.snapsAndWarp(verbose);
468  mesher.stencilTets(verbose);
469  cleaving_timer.stop();
470  cleaving_time = cleaving_timer.time();
471 
472  cleaver::TetMesh *mesh = mesher.getTetMesh();
473 
474  //-----------------------------------------------------------
475  // Strip Exterior Tets
476  //-----------------------------------------------------------
477  if (strip_exterior) {
478  cleaver::stripExteriorTets(mesh, volume, verbose);
479  }
480 
481  //-----------------------------------------------------------
482  // Compute Quality If Havn't Already
483  //-----------------------------------------------------------
484  mesh->computeAngles();
485  //-----------------------------------------------------------
486  // Fix jacobians if requested.
487  //-----------------------------------------------------------
488  if (fix_tets) mesh->fixVertexWindup(verbose);
489 
490  //-----------------------------------------------------------
491  // Write Mesh To File
492  //-----------------------------------------------------------
493  mesh->writeMesh(output_path + output_name, output_format, verbose);
494  mesh->writeInfo(output_path + output_name, verbose);
495  //-----------------------------------------------------------
496  // Write Experiment Info to file
497  //-----------------------------------------------------------
498  total_timer.stop();
499  double total_time = total_timer.time();
500 
501  if (verbose) {
502  std::cout << "Output Info" << std::endl;
503  std::cout << "Size: " << volume->size().toString() << std::endl;
504  std::cout << "Materials: " << volume->numberOfMaterials() << std::endl;
505  std::cout << "Min Dihedral: " << mesh->min_angle << std::endl;
506  std::cout << "Max Dihedral: " << mesh->max_angle << std::endl;
507  std::cout << "Total Time: " << total_time << " seconds" << std::endl;
508  std::cout << "Sizing Field Time: " << sizing_field_time << " seconds" << std::endl;
509  std::cout << "Backound Mesh Time: " << background_time << " seconds" << std::endl;
510  std::cout << "Cleaving Time: " << cleaving_time << " seconds" << std::endl;
511  }
512  return 0;
513 }