Virtual Testbed
Ship dynamics simulator for extreme conditions
assets/main.cc
1 #include <unistd.h>
2 
3 #include <atomic>
4 #include <fstream>
5 #include <iostream>
6 #include <sstream>
7 #include <unordered_set>
8 #include <vector>
9 
10 #include <vtestbed/base/files.hh>
11 #include <vtestbed/config/openmp.hh>
12 #include <vtestbed/config/real_type.hh>
13 #include <vtestbed/assets/convert.hh>
14 
15 namespace vtb {
16 namespace assets {
17 
18 inline std::string
19 make_output_file(const std::string& input_file, const std::string& output_directory,
20  Format to, const Options& options) {
21  auto path = vtb::base::parent_child(input_file);
22  if (!output_directory.empty()) { path.first = output_directory; }
23  std::string output_file;
24  if (path.first != ".") { output_file += path.first; output_file += '/'; }
25  output_file += path.second;
26  output_file += '.';
27  if (options.get<bool>("gnuplot", false)) { output_file += "gnuplot"; }
28  else { output_file += to_extension(to); }
29  return output_file;
30 }
31 
32 class Converter {
33 
34 private:
35  using scalar_type = VTB_REAL_TYPE;
36 
37 private:
38  Format _from{}, _to{};
40  std::string _output_file, _output_directory;
41  Options _options;
42  int _nthreads = 1;
43  bool _verbose = false;
44 
45 public:
46 
47  void
48  arguments(int argc, char* argv[]) {
49  auto& from = this->_from;
50  auto& to = this->_to;
51  auto& files = this->_files;
52  auto& output_file = this->_output_file;
53  int opt;
54  while ((opt = ::getopt(argc, argv, "f:t:o:O:j:d:hv")) != -1) {
55  switch (opt) {
56  case 'f': from = string_to_format(::optarg); break;
57  case 't': to = string_to_format(::optarg); break;
58  case 'o': output_file = ::optarg; break;
59  case 'O': read_option(::optarg); break;
60  case 'h': usage(argv[0]); std::exit(EXIT_SUCCESS); break;
61  case 'v': this->_verbose = true; break;
62  case 'j': this->_nthreads = std::max(1, std::atoi(::optarg)); break;
63  case 'd': this->_output_directory = ::optarg; break;
64  default: usage(argv[0]); std::exit(EXIT_FAILURE); break;
65  }
66  }
67  if (::optind == argc) {
68  throw std::invalid_argument("no files are specified");
69  }
70  files.assign(argv + ::optind, argv + argc);
71  if (files.size() > 1 && !output_file.empty()) {
72  throw std::invalid_argument("single output for many input files");
73  }
74  if (output_file.empty() && to == Format::Unspecified) {
75  throw std::invalid_argument("no output format is specified");
76  }
77  if (from == Format::Unspecified && files.size() == 1) {
78  from = filename_to_format(files.front());
79  }
80  if (to == Format::Unspecified && !output_file.empty()) {
81  to = filename_to_format(output_file);
82  }
83  if (to == Format::Unspecified && from != Format::Unspecified) { to = from; }
84  if (!output_file.empty() && !this->_output_directory.empty()) {
86  "specify either output file or output directory, but not both");
87  }
88  }
89 
90  int run() {
91  std::atomic<int> ret{EXIT_SUCCESS};
92  const auto& to = this->_to;
93  const auto& files = this->_files;
94  auto& output_file = this->_output_file;
95  if (files.size() == 1) {
96  if (output_file.empty()) {
97  output_file = make_output_file(files.front(), this->_output_directory,
98  to, this->_options);
99  }
100  convert_file(files.front(), output_file);
101  } else {
102  const auto nfiles = files.size();
103  #if defined(VTB_WITH_OPENMP)
104  #pragma omp parallel for schedule(dynamic,1) num_threads(this->_nthreads)
105  #endif
106  for (size_t i=0; i<nfiles; ++i) {
107  const auto& file = files[i];
108  try {
109  convert_file(file, make_output_file(file, this->_output_directory,
110  to, this->_options));
111  } catch (const std::exception& err) {
112  std::stringstream msg;
113  msg << err.what() << '\n';
114  std::cerr << msg.str();
115  ret = EXIT_FAILURE;
116  }
117  }
118  }
119  return ret;
120  }
121 
122  void
123  convert_file(const std::string& input_file, const std::string& output_file) {
124  auto from = this->_from == Format::Unspecified
125  ? filename_to_format(input_file) : this->_from;
126  const auto& to = this->_to;
127  if (this->_verbose) {
128  std::stringstream msg;
129  msg << input_file << " -> " << output_file << '\n';
130  std::clog << msg.str();
131  }
132  auto options = this->_options;
133  options["input-path"] = input_file;
134  options["input-format"] = from;
135  options["output-path"] = output_file;
136  options["output-format"] = to;
137  export_asset(convert(import_asset(options), options), options);
138  }
139 
140  void
141  usage(const char* name) {
142  std::cout
143  << "usage: " << name << " [-f from] [-t to] [-o file] [-O option] [-hvj] file...\n"
144  <<
145 R"(-f from source format
146  -t to target format
147  -o file output file
148  -O option set format-specific option
149  -j n the number of parallel threads
150  -v be verbose
151 supported formats: vsl, obj, stl, igs, rms, bsp
152 Options:
153 format option default value description
154 --------------------------------------------------------------------------------
155 vsl close-top true do not remove the main deck
156 vsl close-bottom true do not remove the bottom
157 vsl step-z auto vertical distance between points in metres
158 bsp resolution-u 2 no. of points for segment in U dimension
159 bsp resolution-v 2 no. of points for segment in V dimension
160 * gnuplot false output as gnuplot script)";
161  }
162 
163 private:
164 
165  template <class T>
166  void
167  read_scalar(const char* arg, std::any& result) {
168  std::istringstream str(arg);
169  str.exceptions(std::ios::failbit | std::ios::badbit);
170  T value{};
171  str >> value;
172  result = value;
173  }
174 
175  void read_option(const char* arg) {
176  this->_options.read(arg);
177  }
178 
179 };
180 
181 }
182 }
183 
184 
185 int main(int argc, char* argv[]) {
186  int ret = EXIT_FAILURE;
187  try {
188  vtb::assets::Converter converter;
189  converter.arguments(argc, argv);
190  ret = converter.run();
191  } catch (const std::ios::failure&) {
192  std::cerr << "i/o error" << std::endl;
193  } catch (const std::exception& err) {
194  std::cerr << err.what() << std::endl;
195  }
196  return ret;
197 }
Main namespace.
Definition: convert.hh:9