Bembel
GeometryIGS.hpp
1 // This file is part of Bembel, the higher order C++ boundary element library.
2 //
3 // Copyright (C) 2024 see <http://www.bembel.eu>
4 //
5 // It was written as part of a cooperation of J. Doelz, H. Harbrecht, S. Kurz,
6 // M. Multerer, S. Schoeps, and F. Wolf at Technische Universitaet Darmstadt,
7 // Universitaet Basel, and Universita della Svizzera italiana, Lugano. This
8 // source code is subject to the GNU General Public License version 3 and
9 // provided WITHOUT ANY WARRANTY, see <http://www.bembel.eu> for further
10 // information.
11 
12 #ifndef BEMBEL_SRC_GEOMETRY_GEOMETRYIGS_HPP_
13 #define BEMBEL_SRC_GEOMETRY_GEOMETRYIGS_HPP_
14 
15 namespace Bembel {
16 
25 std::vector<Patch> LoadGeometryFileIGS(const std::string& file_name) noexcept {
26  std::ifstream file;
27  std::vector<int> patch_lines;
28  file.open(file_name);
29  if (!file) {
30  std::cerr << "File " << file_name << " doesn't exist!";
31  exit(1);
32  }
33  std::string row;
34  getline(file, row);
35 
36  if (row[72] != 'S') {
37  std::cerr << "Format of the file not supported! I assume that character 72 "
38  "denotes the section";
39  exit(1);
40  }
41  // character 72 denotes the section and the information starts in Section D
42  while (row[72] != 'D') {
43  getline(file, row);
44  assert(!file.eof() && "End of file should not be found here!");
45  }
46 
47  // collect two lines info of each patch from Directory section
48  while (row[72] != 'P') {
49  std::stringstream iss;
50  std::string word;
51  std::vector<int> info1, info2;
52  iss.str(row);
53  while (iss >> word) {
54  info1.push_back(std::stoi(word));
55  }
56  assert(info1[0] == 128 && "Entry type must be NURBS surface!");
57  // this info1 in not going to be used further
58 
59  getline(file, row);
60  iss.clear();
61  iss.str(row);
62  while (iss >> word) {
63  info2.push_back(std::stoi(word));
64  }
65  getline(file, row);
66  // This entry denotes how many lines correspond to a patch
67  patch_lines.push_back(info2[3]);
68  assert(!file.eof() && "End of file should not be found here!");
69  }
70 
71  // main loop over patches
72  std::vector<Patch> out;
73  out.reserve(patch_lines.size());
74  const int number_of_patches = patch_lines.size();
75  for (auto i = 0; i < number_of_patches; ++i) {
76  std::stringstream iss;
77  std::string word;
78  std::vector<double> data;
79  for (auto j = 0; j < patch_lines[i]; ++j) {
80  // characters 0 to 63 contain data
81  std::string raw_data = row.substr(0, 64);
82  raw_data.erase(std::remove(raw_data.begin(), raw_data.end(), ' '),
83  raw_data.end());
84 
85  iss.str(raw_data);
86  while (std::getline(iss, word, ',')) {
87  data.push_back(atof(word.c_str()));
88  }
89  iss.clear();
90 
91  getline(file, row);
92  }
93 
94  const int K1 = data[1];
95  const int K2 = data[2];
96  const int M1 = data[3];
97  const int M2 = data[4];
98 
99  const int N1 = 1 + K1 - M1;
100  const int N2 = 1 + K2 - M2;
101  const int A = N1 + 2 * M1;
102  const int B = N2 + 2 * M2;
103  const int C = (1 + K1) * (1 + K2);
104 
105  // The data of the first entries is read and not needed any more
106  data.erase(data.begin(), data.begin() + 10);
107 
108  // the + 1 is necessary because the construct excludes the last iterator
109  std::vector<double> tempknt1(data.begin(), data.begin() + A + 1);
110  std::vector<double> tempknt2(data.begin() + A + 1,
111  data.begin() + A + B + 2);
112 
113  const int M = K2 + 1;
114  const int N = K1 + 1;
115 
116  Eigen::MatrixXd weights(M, N);
117  auto it_weights = data.begin() + A + B + 2;
118  for (auto entry = it_weights; entry != it_weights + C; ++entry) {
119  int index = entry - it_weights;
120  weights((int)index / N, index % N) = *entry;
121  }
122 
123  Eigen::MatrixXd x_coordinates(M, N);
124  Eigen::MatrixXd y_coordinates(M, N);
125  Eigen::MatrixXd z_coordinates(M, N);
126  auto it_points = data.begin() + A + B + C + 2;
127  for (auto entry = it_points; entry != it_points + 3 * C; entry += 3) {
128  int index = (int)(entry - it_points) / 3;
129  x_coordinates((int)index / N, index % N) = *entry;
130  y_coordinates((int)index / N, index % N) = *(entry + 1);
131  z_coordinates((int)index / N, index % N) = *(entry + 2);
132  }
133 
134  // we need to transfer to homogeneous coordinates
135  x_coordinates = x_coordinates.cwiseProduct(weights);
136  y_coordinates = y_coordinates.cwiseProduct(weights);
137  z_coordinates = z_coordinates.cwiseProduct(weights);
138 
139  std::vector<Eigen::MatrixXd> tmp = {x_coordinates, y_coordinates,
140  z_coordinates, weights};
141 
142  Bembel::Patch tempPatch;
143  tempPatch.init_Patch(tmp, tempknt1, tempknt2);
144  out.push_back(tempPatch);
145  }
146  file.close();
147  return out;
148 }
149 
158 void writeSection(std::string file_name,
159  const std::vector<std::string>& section,
160  const char section_char) {
161  std::ofstream file(file_name, std::ios::app);
162  for (auto it = section.begin(); it != section.end(); ++it) {
163  const int index = std::distance(section.begin(), it);
164  file << std::left << std::setw(72) << *it << section_char << std::right
165  << std::setw(7) << index + 1 << "\n";
166  }
167  file.close();
168  return;
169 }
170 
181 int writeParameterSection(std::string file_name,
182  const std::vector<std::string>& section,
183  const int patch_idx, const int start_count) {
184  std::ofstream file(file_name, std::ios::app);
185  int last_line = start_count;
186  for (auto it = section.begin(); it != section.end(); ++it) {
187  file << std::left << std::setw(64) << *it
188  << std::right << std::setw(8) << patch_idx << "P"
189  << std::right << std::setw(7) << last_line << "\n";
190  ++last_line;
191  }
192  file.close();
193  return last_line;
194 }
195 
205 std::vector<std::string> makeSection(std::vector<std::string> data,
206  const int linewidth = 72) {
207  std::vector<std::string> out;
208  out.reserve(data.size());
209 
210  std::string line = "";
211  const int number_of_entries = data.size();
212  for (auto i = 0; i < number_of_entries; ++i) {
213  std::string item = data[i];
214  if (i < number_of_entries - 1)
215  item += ",";
216  else
217  item += ";";
218 
219  const int line_length = line.length() + item.length();
220  if (line_length > linewidth) {
221  out.push_back(line);
222  line = "";
223  }
224  line += item;
225  }
226  out.push_back(line);
227  return out;
228 }
229 
236 void writeIGSHeader(std::string file_name) {
237  std::vector<std::string> section(4);
238  section[0] = "";
239  section[1] = "IGES obtained from Bembel.";
240  section[2] = "See <http://www.bembel.eu>";
241  section[3] = "";
242 
243  writeSection(file_name, section, 'S');
244  return;
245 }
246 
253 std::string double_to_string(double d, const int precision) {
254  std::ostringstream stm;
255  stm << std::setprecision(precision) << d;
256  return stm.str();
257 }
258 
265 int writeGlobalSection(std::string file_name) {
266  std::vector<std::string> out(24);
267  std::time_t now = std::time(nullptr);
268 
269  std::tm* localTime = std::localtime(&now);
270  char dateString[11]; // "YYYY-MM-DD\0"
271  std::strftime(dateString, sizeof(dateString), "%Y-%m-%d", localTime);
272  // Parameter Deliminator Character
273  out[0] = "1H,";
274  // Record Delimiter Character
275  out[1] = "1H;";
276  // Product ID from Sender
277  out[2] = "Bembel";
278  // File Name
279  out[3] = file_name;
280  // System ID
281  out[4] = "Bembel";
282  // Pre-processor Version
283  out[5] = "writeIGSFile";
284  // Number of Bits for Integers
285  out[6] = "32";
286  // Single Precision Magnitude
287  out[7] = "75";
288  // Single Precision Significance
289  out[8] = "6";
290  // Double Precision Magnitude
291  out[9] = "75";
292  // Double Precision Significance
293  out[10] = "15";
294  // Product ID for Receiver
295  out[11] = "Nurbs from Bembel";
296  // Model Space Scale
297  out[12] = "1.0";
298  // Unit Flag (6 = metres)
299  out[13] = "6";
300  // Units (metres = "M")
301  out[14] = "M";
302  // Maximum Number of Line Weights
303  out[15] = "1000";
304  // Size of Maximum Line Width
305  out[16] = "1.0";
306  // Date and Time of file generation
307  out[17] = dateString;
308  // Minimum User-intended Resolution
309  out[18] = "0.000001";
310  // Approximate Maximum Coordinate
311  out[19] = "10000.0";
312  // Name of Author
313  out[20] = "Maximilian Nolte";
314  // Author's Organization
315  out[21] = "CEM - TU Darmstadt";
316  // IGES Version Number (3 = IGES version 2.0)
317  out[22] = "3";
318  // Drafting Standard Code (0 = no standard)
319  out[23] = "0";
320 
321  std::vector<std::string> section = makeSection(out);
322 
323  writeSection(file_name, section, 'G');
324  return section.size();
325 }
326 
336 int writeDirectory(std::string file_name, std::vector<int> start_idx,
337  std::vector<int> number_of_lines) {
338  assert(start_idx.size() == number_of_lines.size());
339 
340  std::ofstream fid(file_name, std::ios::app);
341  int tot_number_of_lines = 0;
342  const int number_of_patches = start_idx.size();
343  for (auto i = 0; i < number_of_patches; ++i) {
344  fid << std::setw(8) << "128"
345  << std::setw(8) << start_idx[i]
346  << std::setw(8) << 0
347  << std::setw(8) << 0
348  << std::setw(8) << 0
349  << std::setw(8) << 0
350  << std::setw(8) << 0
351  << std::setw(8) << 0
352  << std::setw(8) << 0
353  << "D"
354  << std::setw(7) << (i + 1) * 2 - 1<< "\n";
355 
356  fid << std::setw(8) << "128"
357  << std::setw(8) << 0
358  << std::setw(8) << 0
359  << std::setw(8) << number_of_lines[i]
360  << std::setw(8) << 0
361  << std::setw(8) << " "
362  << std::setw(8) << " "
363  << std::setw(8) << " "
364  << std::setw(8) << 0
365  << "D"
366  << std::setw(7) << (i + 1) * 2 << "\n";
367 
368  tot_number_of_lines += 2;
369  }
370  return tot_number_of_lines;
371 }
372 
385 std::vector<std::string> writePatchData(const Patch& patch,
386  const int precision) {
387  const int degree_x = patch.polynomial_degree_x_;
388  const int degree_y = patch.polynomial_degree_y_;
389 
390  assert(patch.unique_knots_x_.size() == 2 &&
391  "We can not handle internal knots");
392  assert(patch.unique_knots_y_.size() == 2 &&
393  "We can not handle internal knots");
394 
395  std::vector<double> knots_x(2 * degree_x, 0);
396  for (auto i = 0; i <= degree_x; ++i) knots_x[degree_x + i] = 1;
397  std::vector<double> knots_y(2 * degree_y, 0);
398  for (auto i = 0; i <= degree_y; ++i) knots_y[degree_y + i] = 1;
399 
400  const std::array<double, 2> span_x = {0, 1};
401  const std::array<double, 2> span_y = {0, 1};
402 
403  const int number_of_points_x = knots_x.size() - degree_x;
404  const int number_of_points_y = knots_y.size() - degree_y;
405 
406  const int size = patch.data_.size() / 4;
407  std::vector<double> weights(size);
408  std::vector<double> coordinates_x(size);
409  std::vector<double> coordinates_y(size);
410  std::vector<double> coordinates_z(size);
411  // transform from wx, wy, wz to x, y, z
412  // further more switch row major and column major
413  for (auto i = 0; i < size; ++i) {
414  const int rowIndex = i % number_of_points_y;
415  const int colIndex = i / number_of_points_y;
416 
417  // Calculate the index for the corresponding element in row-major order
418  const int j = rowIndex * number_of_points_x + colIndex;
419 
420  double weight = patch.data_[i * 4 + 3];
421  weights[j] = weight;
422  coordinates_x[j] = patch.data_[i * 4] / weight;
423  coordinates_y[j] = patch.data_[i * 4 + 1] / weight;
424  coordinates_z[j] = patch.data_[i * 4 + 2] / weight;
425  }
426 
427  const int number_of_data_entries = 16;
428  const int size_data =
429  number_of_data_entries + 4 * size + knots_x.size() + knots_y.size();
430  std::vector<std::string> patch_data(size_data);
431 
432  patch_data[0] = "128";
433  patch_data[1] = std::to_string(number_of_points_x - 1);
434  patch_data[2] = std::to_string(number_of_points_y - 1);
435  patch_data[3] = std::to_string(degree_x - 1);
436  patch_data[4] = std::to_string(degree_y - 1);
437  patch_data[5] = "0";
438  patch_data[6] = "0";
439  patch_data[7] = "0";
440  patch_data[8] = "0";
441  patch_data[9] = "0";
442 
443  for (auto i = 0; i < knots_x.size(); ++i) {
444  patch_data[10 + i] = double_to_string(knots_x[i], precision);
445  }
446 
447  const int idx_knots_y = 10 + knots_x.size();
448  for (auto i = 0; i < knots_y.size(); ++i) {
449  patch_data[idx_knots_y + i] = double_to_string(knots_y[i], precision);
450  }
451 
452  const int idx_weights = 10 + knots_x.size() + knots_y.size();
453  for (auto i = 0; i < size; ++i) {
454  patch_data[idx_weights + i] = double_to_string(weights[i], precision);
455 
456  patch_data[idx_weights + size + i * 3] =
457  double_to_string(coordinates_x[i], precision);
458  patch_data[idx_weights + size + i * 3 + 1] =
459  double_to_string(coordinates_y[i], precision);
460  patch_data[idx_weights + size + i * 3 + 2] =
461  double_to_string(coordinates_z[i], precision);
462  }
463  patch_data[idx_weights + 4 * size] = std::to_string(span_x[0]);
464  patch_data[idx_weights + 4 * size + 1] = std::to_string(span_x[1]);
465  patch_data[idx_weights + 4 * size + 2] = std::to_string(span_y[0]);
466  patch_data[idx_weights + 4 * size + 3] = std::to_string(span_y[1]);
467  patch_data[idx_weights + 4 * size + 4] = "0";
468  patch_data[idx_weights + 4 * size + 5] = "0";
469 
470  return patch_data;
471 }
472 
481 void writeIGSFile(const std::vector<Patch>& geometry, std::string file_name,
482  const int precision = 6) {
483  std::ofstream out(file_name);
484  out.close();
485  writeIGSHeader(file_name);
486  const int size_global = writeGlobalSection(file_name);
487 
488  const int number_of_patches = geometry.size();
489  std::vector<int> start_idx(number_of_patches);
490  std::vector<int> end_idx(number_of_patches);
491  std::vector<std::vector<std::string>> patch_sections(number_of_patches);
492  int start_line = 1;
493  for (auto i = 0; i < number_of_patches; ++i) {
494  const std::vector<std::string> patch_data =
495  writePatchData(geometry[i], precision);
496  patch_sections[i] = makeSection(patch_data, 64);
497 
498  start_idx[i] = start_line;
499  start_line += patch_sections[i].size();
500  end_idx[i] = patch_sections[i].size();
501  }
502  const int size_directory = writeDirectory(file_name, start_idx, end_idx);
503 
504  int first_line = 1;
505  int size_parameter = 0;
506  for (auto it = patch_sections.begin(); it != patch_sections.end(); ++it) {
507  const int i = std::distance(patch_sections.begin(), it);
508  const int last_idx =
509  writeParameterSection(file_name, *it, 1 + 2 * i, first_line);
510  first_line = last_idx;
511  size_parameter += (*it).size();
512  }
513 
514  // Terminate Section
515  std::stringstream last_section;
516  last_section << std::setw(7) << 4 << "S"
517  << std::setw(7) << size_global << "G"
518  << std::setw(7) << size_directory << "D"
519  << std::setw(7) << size_parameter << "P";
520 
521 
522  std::ofstream file(file_name, std::ios::app);
523  file << std::left << std::setw(72) << last_section.str()
524  << "T"
525  << std::right << std::setw(7) << 1 << "\n";
526  file.close();
527 
528  return;
529 }
530 } // namespace Bembel
531 #endif // BEMBEL_SRC_GEOMETRY_GEOMETRYIGS_HPP_
handles a single patch
Definition: Patch.hpp:20
void init_Patch(const std::vector< Eigen::Matrix< double, -1, -1 >> &xyzw, const std::vector< double > &x_knots, const std::vector< double > &y_knots)
init
Definition: Patch.hpp:63
std::vector< double > data_
getter
Definition: Patch.hpp:371
int writeDirectory(std::string file_name, std::vector< int > start_idx, std::vector< int > number_of_lines)
Write the IGES Directory section into the given file.
int writeParameterSection(std::string file_name, const std::vector< std::string > &section, const int patch_idx, const int start_count)
This function writes a the parameter section of the IGES file.
void writeIGSHeader(std::string file_name)
Write the IGES header into the given file.
void writeSection(std::string file_name, const std::vector< std::string > &section, const char section_char)
This function writes a given section to the specified file.
std::vector< Patch > LoadGeometryFileIGS(const std::string &file_name) noexcept
loads geometry from IGES file. Note that the direction of the normals must be consistent.
Definition: GeometryIGS.hpp:25
std::vector< std::string > makeSection(std::vector< std::string > data, const int linewidth=72)
Transform a vector of Data in a vector of lines with a specific length.
std::vector< std::string > writePatchData(const Patch &patch, const int precision)
This Function writes the patch from Bembel into a vector which can be written into an IGES file.
void writeIGSFile(const std::vector< Patch > &geometry, std::string file_name, const int precision=6)
Writes Geometry into an IGES file format.
int writeGlobalSection(std::string file_name)
Write the IGES Global section into the given file.
Routines for the evalutation of pointwise errors.
Definition: AnsatzSpace.hpp:14
std::string double_to_string(double d, const int precision)
Convert a double to a string with given precision.