Virtual Testbed
Ship dynamics simulator for extreme conditions
geometry/polyhedron.cc
1 #include <algorithm>
2 #include <limits>
3 #include <map>
4 #include <ostream>
5 #include <queue>
6 #include <set>
7 
8 #include <vtestbed/base/constants.hh>
9 #include <vtestbed/base/mean.hh>
10 #include <vtestbed/config/real_type.hh>
11 #include <vtestbed/core/bisection.hh>
13 #include <vtestbed/geometry/inertia_tensor.hh>
14 #include <vtestbed/geometry/math.hh>
15 #include <vtestbed/geometry/polyhedron.hh>
16 #include <vtestbed/geometry/tetrahedron.hh>
17 #include <vtestbed/geometry/vertex.hh>
18 
19 //#define VTB_DEBUG_DEDUPLICATE
20 
21 namespace {
22 
23  template <class T>
24  inline blitz::Array<T,1>
25  to_blitz_array(const std::vector<T>& src) {
26  const auto n = src.size();
27  blitz::Array<T,1> result(blitz::shape(n));
28  for (size_t i=0; i<n; ++i) {
29  result(i) = src[i];
30  }
31  return result;
32  }
33 
34  template <class T>
35  inline void
36  flip_vertices(std::vector<T>& vertices, int dim) {
37  for (auto& v : vertices) {
38  v(dim) = -v(dim);
39  }
40  }
41 
42  template <class T>
43  inline void
44  mirror_vertices(std::vector<T>& vertices, int dim) {
45  const auto nvertices = vertices.size();
46  vertices.resize(nvertices*2);
47  for (size_t i=0; i<nvertices; ++i) {
48  auto v = vertices[i];
49  v(dim) = -v(dim);
50  vertices[nvertices + i] = v;
51  }
52  }
53 
54  template <class Vertices, class Normals, class Rotate>
55  inline void
56  rotate_vertices(Vertices& vertices, Normals& normals, Rotate rotate) {
57  for (auto& v : vertices) {
58  v = rotate(v);
59  }
60  for (auto& v : normals) {
61  v = rotate(v);
62  }
63  }
64 
65  template <int N>
66  struct CompareFace {
67 
68  using value_type = vtb::geometry::Face<N>;
69 
70  inline bool
71  operator()(value_type lhs, value_type rhs) const {
72  lhs.sort(), rhs.sort();
73  return lhs < rhs;
74  }
75 
76  };
77 
78  inline bool
79  face_has_duplicate_indices(const vtb::geometry::Face<3>& f) {
80  return f[0] == f[1] || f[1] == f[2] || f[2] == f[0];
81  }
82 
83  template <class T, int N>
84  struct CompareTriangles {
85 
86  using value_type = vtb::geometry::Triangle<T,N>;
87 
88  inline bool
89  operator()(const value_type& lhs, const value_type& rhs) const {
90  const auto y0 = std::min({lhs[0][1], lhs[1][1], lhs[2][1]});
91  const auto y1 = std::min({rhs[0][1], rhs[1][1], rhs[2][1]});
92  return y0 < y1;
93  }
94 
95  };
96 
97  template <class T, int N>
98  void
99  gnuplot(std::ostream& out, const vtb::geometry::Vertex<T,N>& v) {
100  out << v(0);
101  for (int i=1; i<N; ++i) { out << ' ' << v(i); }
102  }
103 
104 }
105 
106 template <class T, int N>
108  const blitz_vertex_array& vertices,
109  const blitz_face_array& faces
110 ):
111 _vertices(vertices.data(), vertices.data() + vertices.size()),
112 _faces(faces.data(), faces.data() + faces.size()) {}
113 
114 
115 template <class T, int N>
116 vtb::geometry::Polyhedron<T,N>::Polyhedron(const bsp_tree& tree) {
117  auto& vertices = this->_vertices;
118  auto& faces = this->_faces;
119  index_type idx = 0;
120  const auto n = tree.nodes().size();
121  vertices.reserve(n*3);
122  faces.reserve(n);
123  for (const auto& node : tree.nodes()) {
124  for (const auto& tr : node.polygons()) {
125  vertices.emplace_back(tr[0]);
126  vertices.emplace_back(tr[1]);
127  vertices.emplace_back(tr[2]);
128  faces.emplace_back(idx, idx+1, idx+2);
129  idx += 3;
130  }
131  }
132 }
133 
134 template <class T, int N>
135 vtb::geometry::Polyhedron<T,N>::Polyhedron(const triangle_array& triangles) {
136  auto& vertices = this->_vertices;
137  auto& faces = this->_faces;
138  index_type idx = 0;
139  vertices.reserve(triangles.size()*3);
140  faces.reserve(triangles.size());
141  for (const auto& tr : triangles) {
142  vertices.emplace_back(tr[0]);
143  vertices.emplace_back(tr[1]);
144  vertices.emplace_back(tr[2]);
145  faces.emplace_back(idx, idx+1, idx+2);
146  idx += 3;
147  }
148 }
149 
150 template <class T, int N>
151 void
153  const auto& vertices = this->_vertices;
154  const auto& faces = this->_faces;
155  const auto nfaces = faces.size();
156  auto& vnormals = this->_vertex_normals;
157  vnormals.assign(vertices.size(), T{0});
158  auto& fnormals = this->_face_normals;
159  fnormals.assign(faces.size(), T{0});
160  for (size_type i=0; i<nfaces; ++i) {
161  const auto& face = this->_faces[i];
162  const auto i0 = face[0];
163  const auto i1 = face[1];
164  const auto i2 = face[2];
165  const auto& v0 = vertices[i0];
166  const auto& v1 = vertices[i1];
167  const auto& v2 = vertices[i2];
168  vertex_type a = v1-v0, b = v2-v0;
169  vertex_type n = cross(a, b);
170  vnormals[i0] += n, vnormals[i1] += n, vnormals[i2] += n;
171  fnormals[i] = unit(n);
172  }
173  for (auto& n : vnormals) {
174  n = unit(n);
175  }
176 }
177 
178 template <class T, int N>
179 void
181  const auto& vertices = this->_vertices;
182  const auto& faces = this->_faces;
183  const auto nfaces = faces.size();
184  auto& fnormals = this->_face_normals;
185  fnormals.assign(faces.size(), T{0});
186  for (size_type i=0; i<nfaces; ++i) {
187  const auto& face = this->_faces[i];
188  const auto i0 = face[0];
189  const auto i1 = face[1];
190  const auto i2 = face[2];
191  const auto& v0 = vertices[i0];
192  const auto& v1 = vertices[i1];
193  const auto& v2 = vertices[i2];
194  vertex_type a = v1-v0, b = v2-v0;
195  vertex_type n = cross(a, b);
196  fnormals[i] = unit(n);
197  }
198 }
199 
200 template <class T, int N>
201 void
203  using vtb::geometry::centroid;
204  const auto& vf = this->vertex_faces();
205  const auto& cf = this->component_faces(vf);
206  auto& faces = this->_faces;
207  const auto& origin = centroid(vertices());
208  index_type nfaces = faces.size();
209  for (const auto& face_indices : cf) {
210  index_type rfi = this->reference_face_index(face_indices, origin);
211  if (rfi == std::numeric_limits<index_type>::max()) { continue; }
212  this->reorder(rfi, origin);
214  std::vector<bool> computed(nfaces, false);
215  queue.push(rfi);
216  computed[rfi] = true;
217  while (!queue.empty()) {
218  const auto& face_a = faces[queue.front()];
219  queue.pop();
220  for (index_type vertex_index : face_a){
221  for (index_type i : vf[vertex_index]){
222  if (computed[i]) { continue; }
223  if (!faces[i].reorder(face_a, vertex_index)) { continue; }
224  computed[i] = true;
225  queue.push(i);
226  }
227  }
228  }
229  }
230 }
231 
232 template <class T, int N>
233 void
235  #if defined(VTB_DEBUG_DEDUPLICATE)
236  size_type old_nfaces = this->_faces.size();
237  size_type old_nvertices = this->_vertices.size();
238  #endif
239  this->remove_duplicate_vertices();
240  this->remove_duplicate_faces();
241  #if defined(VTB_DEBUG_DEDUPLICATE)
242  size_type new_nfaces = this->_faces.size();
243  size_type new_nvertices = this->_vertices.size();
244  std::clog << "removed " << (old_nfaces-new_nfaces)
245  << " faces and " << (old_nvertices-new_nvertices)
246  << " vertices" << std::endl;
247  #endif
248 }
249 
250 template <class T, int N>
251 void
254  using iterator = typename map_type::iterator;
255  auto& vertices = this->_vertices;
256  auto& vertex_normals = this->_vertex_normals;
257  auto& faces = this->_faces;
258  if (!vertex_normals.empty()) {
259  throw std::invalid_argument("vertex normals are not empty");
260  }
261  map_type unique_vertices;
262  vertex_array new_vertex_normals;
263  auto insert = [&] (const index_type& i) -> index_type {
264  const auto& v = vertices[i];
265  iterator it; bool success;
266  index_type index = unique_vertices.size();
267  std::tie(it,success) = unique_vertices.insert({v, index});
268  return !success ? it->second : index;
269  };
270  for (auto& f : faces) {
271  auto i0 = insert(f[0]);
272  auto i1 = insert(f[1]);
273  auto i2 = insert(f[2]);
274  f = face_type(i0,i1,i2);
275  }
276  vertices.resize(unique_vertices.size());
277  for (const auto& entry : unique_vertices) {
278  vertices[entry.second] = entry.first;
279  }
280 }
281 
282 template <class T, int N>
283 void
285  auto& faces = this->_faces;
286  std::set<face_type,CompareFace<3>> unique_faces;
287  for (const auto& f : faces) {
288  if (face_has_duplicate_indices(f)) { continue; }
289  unique_faces.insert(f);
290  }
291  faces.assign(unique_faces.begin(), unique_faces.end());
292 }
293 
294 template <class T, int N>
295 void
297  auto& faces = this->_faces;
298  std::set<face_type,CompareFace<3>> unique_faces, trash;
299  for (const auto& f : faces) {
300  if (face_has_duplicate_indices(f)) { continue; }
301  auto result = unique_faces.insert(f);
302  if (!result.second) { trash.insert(*result.first); }
303  }
304  auto end = trash.end();
305  auto first = unique_faces.begin();
306  while (first != unique_faces.end()) {
307  if (trash.find(*first) != end) { first = unique_faces.erase(first); }
308  else { ++first; }
309  }
310  faces.assign(unique_faces.begin(), unique_faces.end());
311 }
312 
313 template <class T, int N>
314 void
315 vtb::geometry::Polyhedron<T,N>::remove_face(const vertex_type& vertex) {
316  auto& vertices = this->_vertices;
317  auto first = vertices.begin();
318  Equal_vertex<T,N> eq;
319  while (first != vertices.end()) {
320  if (eq(*first, vertex)) {
321  index_type idx = first - vertices.begin();
322  remove_face(idx);
323  }
324  ++first;
325  }
326 }
327 
328 template <class T, int N>
329 void
331  auto& faces = this->_faces;
332  faces.erase(
333  std::remove_if(
334  faces.begin(), faces.end(),
335  [vertex] (const face_type& f) { return f.contains(vertex); }
336  ),
337  faces.end()
338  );
339 }
340 
341 template <class T, int N>
342 void
344  auto& vertices = this->_vertices;
345  auto& vertex_normals = this->_vertex_normals;
346  auto& faces = this->_faces;
347  auto& face_normals = this->_face_normals;
348  const auto offset = vertices.size();
349  for (const auto& v : rhs.vertices()) { vertices.emplace_back(v); }
350  for (const auto& n : rhs.vertex_normals()) {
351  vertex_normals.emplace_back(n);
352  }
353  for (auto f : rhs.faces()) { faces.emplace_back(f += offset); }
354  for (const auto& n : rhs.face_normals()) {
355  face_normals.emplace_back(n);
356  }
357 }
358 
359 template <class T, int N>
360 auto
361 vtb::geometry::Polyhedron<T,N>::vertex_faces() const -> index_array_2d {
362  index_array_2d vf(this->_vertices.size());
363  const auto& faces = this->_faces;
364  int nfaces = faces.size();
365  for (int i=0; i<nfaces; ++i) {
366  const auto& face = faces[i];
367  vf[face[0]].push_back(i);
368  vf[face[1]].push_back(i);
369  vf[face[2]].push_back(i);
370  }
371  return vf;
372 }
373 
374 template <class T, int N>
375 auto
377  const index_array_2d& vf
378 ) const -> index_array_2d {
379  const auto& faces = this->_faces;
380  index_type nfaces = faces.size();
382  std::vector<bool> computed(nfaces, false);
383  index_array_2d cf;
384  for (index_type i=0; i<nfaces; ++i) {
385  if (computed[i]) { continue; }
386  queue.push(i);
387  computed[i] = true;
388  cf.emplace_back();
389  auto& face_indices = cf.back();
390  while (!queue.empty()) {
391  auto face_index = queue.front();
392  queue.pop();
393  face_indices.push_back(face_index);
394  for (index_type vertex_index : faces[face_index]) {
395  for (index_type face : vf[vertex_index]){
396  if (computed[face]) { continue; }
397  computed[face] = true;
398  queue.push(face);
399  }
400  }
401  }
402  }
403  return cf;
404 }
405 
406 template <class T, int N>
407 auto
409  const index_array& indices,
410  const vertex_type& origin
411 ) const -> index_type {
412  const auto& vertices = this->_vertices;
413  const auto& faces = this->_faces;
414 // vertex_type origin = T{0};
415 // for (int i : indices) {
416 // const auto& face = faces[i];
417 // const auto& v0 = vertices[face[0]];
418 // const auto& v1 = vertices[face[1]];
419 // const auto& v2 = vertices[face[2]];
420 // origin += (v0 + v1 + v2) / 3;
421 // }
422 // origin /= indices.size();
423  T min_length = std::numeric_limits<T>::max();
424  index_type face_with_min_length = std::numeric_limits<index_type>::max();
425  for (index_type i : indices) {
426  const auto& face = faces[i];
427  const auto& v0 = vertices[face[0]];
428  const auto& v1 = vertices[face[1]];
429  const auto& v2 = vertices[face[2]];
430  vertex_type face_centre = (v0+v1+v2) / 3;
431  T new_length = distance(face_centre, origin);
432 // T new_length = std::min({v0(1),v1(1),v2(1)});
433  if (new_length < min_length) {
434  min_length = new_length;
435  face_with_min_length = i;
436  }
437  }
438  return face_with_min_length;
439 }
440 
441 template <class T, int N>
442 void
444  index_type reference_face_index,
445  const vertex_type& geometry_centre
446 ) {
447  const auto& vertices = this->_vertices;
448  auto& faces = this->_faces;
449  auto& face = faces[reference_face_index];
450  const auto& v0 = vertices[face[0]];
451  const auto& v1 = vertices[face[1]];
452  const auto& v2 = vertices[face[2]];
453  vertex_type face_centre = (v0+v1+v2) / 3;
454  vertex_type n = surface_normal(v0,v1,v2);
455  if (dot(n, face_centre-geometry_centre) < 0) {
456  face.flip_winding();
457  }
458 }
459 
460 template <class T, int N>
461 void
463  this->_vertices.clear();
464  this->_vertex_normals.clear();
465  this->_faces.clear();
466  this->_face_normals.clear();
467 }
468 
469 template <class T, int N>
470 void
472  this->_vertex_normals.clear();
473  this->_face_normals.clear();
474 }
475 
476 template <class T, int N>
477 void
479  this->_vertices.shrink_to_fit();
480  this->_vertex_normals.shrink_to_fit();
481  this->_faces.shrink_to_fit();
482  this->_face_normals.shrink_to_fit();
483 }
484 
485 template <class T, int N>
486 void
487 vtb::geometry::Polyhedron<T,N>::swap(Polyhedron<T,N>& rhs) {
488  using std::swap;
489  swap(this->_vertices, rhs._vertices);
490  swap(this->_vertex_normals, rhs._vertex_normals);
491  swap(this->_faces, rhs._faces);
492  swap(this->_face_normals, rhs._face_normals);
493 }
494 
495 template <class T, int N>
496 T
498  using vtb::base::Mean;
499  Mean<T> sum;
500  const auto& v = this->_vertices;
501  for (const auto& f : this->_faces) {
502  sum.update(area(Triangle<T,3>{v[f[0]], v[f[1]], v[f[2]]}));
503  }
504  return sum.mean();
505 }
506 
507 template <class T, int N>
508 T
510  using vtb::geometry::signed_volume;
511  vertex_type origin = this->centre();
512  T volume{};
513  const auto& v = this->_vertices;
514  const auto& faces = this->_faces;
515  for (const auto& f : faces) {
516  Tetrahedron<T,N> t{origin, v[f[0]], v[f[1]], v[f[2]]};
517  volume += signed_volume(t);
518  }
519  return volume;
520 }
521 
522 template <class T, int N>
523 T
525  using vtb::geometry::signed_volume;
526  vertex_type origin = this->centre();
527  origin(d) = level;
528  T volume{};
529  const auto& v = this->_vertices;
530  const auto& faces = this->_faces;
531  for (const auto& f : faces) {
532  const auto v0 = v[f[0]];
533  const auto v1 = v[f[1]];
534  const auto v2 = v[f[2]];
535  if (v0(d) > level || v1(d) > level || v2(d) > level) { continue; }
536  Tetrahedron<T,N> t{origin,v0,v1,v2};
537  volume += signed_volume(t);
538  }
539  return volume;
540 }
541 
542 template <class T, int N>
543 auto
544 vtb::geometry::Polyhedron<T,N>::centre() const -> vertex_type {
545  vertex_type origin{T{}};
546  for (const auto& v : vertices()) { origin += v; }
547  const auto nvertices = vertices().size();
548  if (nvertices > 0) { origin /= nvertices; }
549  return origin;
550 }
551 
552 template <class T, int N>
553 auto
554 vtb::geometry::Polyhedron<T,N>::centroid() const -> vertex_type {
555  return mass_moments().centre;
556 }
557 
558 template <class T, int N>
559 auto
561  vertex_type min{std::numeric_limits<T>::max()};
562  vertex_type max{std::numeric_limits<T>::lowest()};
563  for (const auto& v : this->_vertices) {
564  for (int i=0; i<N; ++i) {
565  T m = v(i);
566  if (m < min(i)) {
567  min(i) = m;
568  }
569  if (max(i) < m) {
570  max(i) = m;
571  }
572  }
573  }
574  return Rectangle<T,N>{min,max};
575 }
576 
577 template <class T, int N>
578 auto
579 vtb::geometry::Polyhedron<T,N>::bounds(int dimension) const -> Bounds<T> {
580  scalar_type min = std::numeric_limits<T>::max();
581  scalar_type max = std::numeric_limits<T>::lowest();
582  for (const auto& v : this->_vertices) {
583  T m = v(dimension);
584  if (m < min) { min = m; }
585  if (max < m) { max = m; }
586  }
587  return Bounds<T>{min,max};
588 }
589 
590 template <class T, int N>
591 auto
592 vtb::geometry::Polyhedron<T,N>::triangles() const -> triangle_array {
593  triangle_array triangles;
594  const auto& v = this->_vertices;
595  const auto& faces = this->_faces;
596  for (const auto& f : faces) {
597  triangles.emplace_back(v[f[0]], v[f[1]], v[f[2]]);
598  }
599  return triangles;
600 }
601 
602 template <class T, int N>
603 auto
604 vtb::geometry::Polyhedron<T,N>::blitz_vertices() const -> blitz_vertex_array {
605  return to_blitz_array(this->_vertices);
606 }
607 
608 template <class T, int N>
609 auto
610 vtb::geometry::Polyhedron<T,N>::blitz_vertex_normals() const -> blitz_vertex_array {
611  return to_blitz_array(this->_vertex_normals);
612 }
613 
614 template <class T, int N>
615 auto
616 vtb::geometry::Polyhedron<T,N>::blitz_faces() const -> blitz_face_array {
617  return to_blitz_array(this->_faces);
618 }
619 
620 template <class T, int N>
621 void
623  move(-mass_moments().centre);
624 }
625 
626 template <class T, int N>
627 void
629  //move_to_centre_of_mass();
630  using base::constants;
631  using vtb::core::Bisection;
632  using vtb::geometry::centroid;
633  using vtb::geometry::signed_volume;
634  using bool3 = Vertex<bool,3>;
635  using vec3 = Vertex<T,3>;
636  auto level = bounding_box().min()(2) + draught;
637  auto mass = std::abs(signed_volume_below(2, level)) * constants<T>::water_density();
638  auto goal_function = [&] (T delta) -> T {
639  auto poly = *this;
640  poly.move({0,0,delta});
641  vec3 centre_of_gravity = T{};
642  vec3 origin = centre_of_gravity;
643  origin(2) = 0;
644  vec3 centre_of_floatation{T{}}, centre_of_buoyancy{T{}}, centre_of_wind{T{}};
645  vec3 wind_force{T{}}, buoyancy_force{T{}};
646  T waterline_length = 0, total_dry_volume = 0, total_wet_volume = 0;
647  const auto& vertices = poly._vertices;
648  const auto& faces = poly._faces;
649  constexpr auto p0 = constants<T>::atmospheric_pressure();
650  constexpr auto g = constants<T>::g();
651  constexpr auto air_density = constants<T>::air_density();
652  constexpr auto water_density = constants<T>::water_density();
653  for (const auto& f : faces) {
654  Triangle<T,3> tr{vertices[f[0]],vertices[f[1]],vertices[f[2]]};
655  auto n = surface_normal(tr);
656  auto area = vtb::geometry::area(tr);
657  auto volume = signed_volume(Tetrahedron<T,3>(origin,tr));
658  auto centre = centroid(tr);
659  T wet_area = 0, dry_area = 0;
660  T wet_volume = 0, dry_volume = 0;
661  bool3 above{tr[0][2] > 0, tr[1][2] > 0, tr[2][2] > 0};
662  vec3 wet_centre{T{}}, dry_centre{T{}};
663  bool all_dry = all(above);
664  bool all_wet = !any(above);
665  if (!all_wet && !all_dry) {
666  vec3 a, b;
667  bool x01 = above(0) ^ above(1);
668  bool x02 = above(0) ^ above(2);
669  bool x12 = above(1) ^ above(2);
670  int outstanding = 0;
671  if (x01 && x02) { outstanding = 0, a = tr[1], b = tr[2]; }
672  else if (x01 && x12) { outstanding = 1, a = tr[0], b = tr[2]; }
673  else { outstanding = 2, a = tr[0], b = tr[1]; }
674  const auto& outstanding_vertex = tr[outstanding];
675  Triangle<T,3> subtr{
676  outstanding_vertex,
677  a + (0-a(2))/(outstanding_vertex(2)-a(2))*(outstanding_vertex-a),
678  b + (0-b(2))/(outstanding_vertex(2)-b(2))*(outstanding_vertex-b),
679  };
680  auto subarea = vtb::geometry::area(subtr);
681  {
682  Line_segment<T,3> waterline_segment(subtr[1],subtr[2]);
683  T segment_length = waterline_segment.length();
684  centre_of_floatation += waterline_segment.centre()*segment_length;
685  waterline_length += segment_length;
686  }
687  auto new_volume = signed_volume(Tetrahedron<T,3>(origin,subtr));
688  if (outstanding_vertex(2) > 0) {
689  wet_area = area - subarea;
690  wet_volume = volume - new_volume;
691  wet_centre = T{0.25} * (subtr[1] + subtr[2] + a + b);
692  dry_area = subarea;
693  dry_volume = new_volume;
694  dry_centre = centroid(subtr);
695  } else {
696  wet_area = subarea;
697  wet_volume = new_volume;
698  wet_centre = centroid(subtr);
699  dry_area = area - subarea;
700  dry_volume = volume - new_volume;
701  dry_centre = T{0.25} * (subtr[1] + subtr[2] + a + b);
702  }
703  }
704  if (all_dry) {
705  dry_centre = centre, dry_area = area, dry_volume = volume;
706  }
707  if (all_wet) {
708  wet_centre = centre, wet_area = area, wet_volume = volume;
709  }
710  //n = unit(n);
711  if (dry_area != 0) {
712  auto z = dry_centre(2);
713  centre_of_wind += dry_centre*dry_volume;
714  total_dry_volume += dry_volume;
715  wind_force += (p0 - air_density*g*z) * (-n) * dry_area;
716  }
717  if (wet_area != 0) {
718  auto z = wet_centre(2);
719  centre_of_buoyancy += wet_centre*wet_volume;
720  total_wet_volume += wet_volume;
721  buoyancy_force += (p0 - water_density*g*z) * (-n) * wet_area;
722  }
723  }
724  buoyancy_force /= mass, wind_force /= mass;
725  if (waterline_length != 0) { centre_of_floatation /= waterline_length; }
726  if (total_wet_volume != 0) { centre_of_buoyancy /= total_wet_volume; }
727  if (total_dry_volume != 0) { centre_of_wind /= total_dry_volume; }
728  vec3 gravity_force{0,0,-g};
729  vec3 force = gravity_force + buoyancy_force + wind_force;
730  //const auto& G = centre_of_gravity;
731  //const auto& B = centre_of_buoyancy;
732  //const auto& W = centre_of_wind;
733  //const auto& O = centre_of_floatation;
734  //vec3 torque = cross(vec3(G-O), gravity_force) +
735  // cross(vec3(B-O), buoyancy_force) +
736  // cross(vec3(W-O), wind_force);
737  return force(2);
738  };
739  Bisection<T,1> bisection;
740  bisection.max_iterations(100);
741  bisection.argument_precision(T{1e-5});
742  bisection.function_precision(T{1e-5});
743  auto bounds = bounding_box();
744  auto delta = bisection(bounds.min()(2), bounds.max()(2), goal_function);
745  move({0,0,delta});
746 }
747 
748 template <class T, int N>
749 void
751  using vtb::geometry::centroid;
752  move(-centroid(bounding_box()));
753 }
754 
755 template <class T, int N>
756 void
757 vtb::geometry::Polyhedron<T,N>::move(const vertex_type& delta) {
758  for (auto& v : this->_vertices) { v += delta; }
759 }
760 
761 template <class T, int N>
762 void
763 vtb::geometry::Polyhedron<T,N>::scale(const vertex_type& amount) {
764  for (auto& v : this->_vertices) { v *= amount; }
765 }
766 
767 template <class T, int N>
768 void
769 vtb::geometry::Polyhedron<T,N>::wall(scalar_type thickness) {
770  if (vertices().empty()) { return; }
771  for (auto& v : this->_vertices) {
772  auto v_length = length(v);
773  auto s = std::max(T{}, (v_length-thickness)/v_length);
774  v *= s;
775  }
776 }
777 
778 template <class T, int N>
779 void
781  flip_vertices(this->_vertices, dim);
782  flip_vertices(this->_vertex_normals, dim);
783 }
784 
785 template <class T, int N>
786 void
788  auto& vertices = this->_vertices;
789  const auto nvertices = vertices.size();
790  if (nvertices == 0) {
791  return;
792  }
793  T min = std::numeric_limits<T>::max();
794  for (const auto& v : vertices) {
795  if (v(dim) < min) {
796  min = v(dim);
797  }
798  }
799  this->move(-min);
800  mirror_vertices(vertices, dim);
801  auto& faces = this->_faces;
802  const auto nfaces = faces.size();
803  if (nfaces != 0) {
804  faces.resize(nfaces*2);
805  for (size_t i=0; i<nfaces; ++i) {
806  auto f = faces[i];
807  f += nvertices;
808  faces[nfaces + i] = f;
809  }
810  }
811  if (!this->_vertex_normals.empty()) {
812  mirror_vertices(this->_vertex_normals, dim);
813  }
814 }
815 
816 template <class T, int N>
817 void
819  using vtb::geometry::rotate;
820  if (dim < 0 || dim > 2) {
821  throw std::invalid_argument{"bad dimension"};
822  }
823  if (degrees % 90 != 0) {
824  throw std::invalid_argument{"bad degrees"};
825  }
826  if (degrees == 0) {
827  return;
828  }
829  degrees %= 360;
830  if (degrees < 0) {
831  degrees += 360;
832  }
833  auto& vertices = this->_vertices;
834  auto& normals = this->_vertex_normals;
835  if (degrees == 90) {
836  if (dim == 0) {
837  rotate_vertices(vertices, normals, rotate<0,90,T,N>);
838  } else if (dim == 1) {
839  rotate_vertices(vertices, normals, rotate<1,90,T,N>);
840  } else {
841  rotate_vertices(vertices, normals, rotate<2,90,T,N>);
842  }
843  } else if (degrees == 180) {
844  if (dim == 0) {
845  rotate_vertices(vertices, normals, rotate<0,180,T,N>);
846  } else if (dim == 1) {
847  rotate_vertices(vertices, normals, rotate<1,180,T,N>);
848  } else {
849  rotate_vertices(vertices, normals, rotate<2,180,T,N>);
850  }
851  } else {
852  if (dim == 0) {
853  rotate_vertices(vertices, normals, rotate<0,270,T,N>);
854  } else if (dim == 1) {
855  rotate_vertices(vertices, normals, rotate<1,270,T,N>);
856  } else {
857  rotate_vertices(vertices, normals, rotate<2,270,T,N>);
858  }
859  }
860 }
861 
862 template <class T, int N>
863 auto
865 ::mass_moments_below(int d, scalar_type level) const -> Mass_moments<T,N> {
866  #define VTB_GEOMETRY_SUBEXPRESSIONS(w0,w1,w2, f1,f2,f3, g0,g1,g2) \
867  tmp0 = w0+w1; f1 = tmp0+w2; \
868  tmp1 = w0*w0; tmp2 = tmp1+w1*tmp0; f2 = tmp2+w2*f1; \
869  f3 = w0*tmp1 + w1*tmp2 + w2*f2; \
870  g0 = f2+w0*(f1+w0); g1 = f2+w1*(f1+w1); g2 = f2+w2*(f1+w2)
871  static const T mult[10] = {
872  T{1}/T{6},
873  T{1}/T{24}, T{1}/T{24}, T{1}/T{24},
874  T{1}/T{60}, T{1}/T{60}, T{1}/T{60},
875  T{1}/T{120}, T{1}/T{120}, T{1}/T{120},
876  };
877  T intg[10] = {}; // 1, x, y, z, x^2, y^2, z^2, xy, yz, zy
878  const auto& v = this->_vertices;
879  const auto& faces = this->_faces;
880  for (const auto& f : faces) {
881  // vertices
882  Triangle<T,N> t{v[f[0]], v[f[1]], v[f[2]]};
883  if (t[0][d] > level || t[1][d] > level || t[2][d] > level) { continue; }
884  auto x0 = t[0][0], y0 = t[0][1], z0 = t[0][2];
885  auto x1 = t[1][0], y1 = t[1][1], z1 = t[1][2];
886  auto x2 = t[2][0], y2 = t[2][1], z2 = t[2][2];
887  // edges
888  auto a1 = x1-x0, b1 = y1-y0, c1 = z1-z0;
889  auto a2 = x2-x0, b2 = y2-y0, c2 = z2-z0;
890  // cross products
891  auto d0 = b1*c2-b2*c1, d1 = a2*c1-a1*c2, d2 = a1*b2-a2*b1;
892  // common subexpressions
893  T tmp0, tmp1, tmp2;
894  T f1x, f2x, f3x, f1y, f2y, f3y, f1z, f2z, f3z;
895  T g0x, g1x, g2x, g0y, g1y, g2y, g0z, g1z, g2z;
896  VTB_GEOMETRY_SUBEXPRESSIONS(x0,x1,x2, f1x,f2x,f3x, g0x,g1x,g2x);
897  VTB_GEOMETRY_SUBEXPRESSIONS(y0,y1,y2, f1y,f2y,f3y, g0y,g1y,g2y);
898  VTB_GEOMETRY_SUBEXPRESSIONS(z0,z1,z2, f1z,f2z,f3z, g0z,g1z,g2z);
899  // integrals
900  intg[0] += d0*f1x;
901  intg[1] += d0*f2x; intg[2] += d1*f2y; intg[3] += d2*f2z;
902  intg[4] += d0*f3x; intg[5] += d1*f3y; intg[6] += d2*f3z;
903  intg[7] += d0*(y0*g0x + y1*g1x + y2*g2x);
904  intg[8] += d1*(z0*g0y + z1*g1y + z2*g2y);
905  intg[9] += d2*(x0*g0z + x1*g1z + x2*g2z);
906  }
907  for (int i=0; i<10; ++i) { intg[i] *= mult[i]; }
908  Mass_moments<T,N> prop;
909  auto& cm = prop.centre;
910  auto& mass = prop.volume;
911  mass = intg[0];
912  cm(0) = intg[1];
913  cm(1) = intg[2];
914  cm(2) = intg[3];
915  if (mass != 0) { cm /= mass; }
916  prop.inertia.xx = intg[5] + intg[6] - mass*(cm(1)*cm(1)+cm(2)*cm(2));
917  prop.inertia.yy = intg[4] + intg[6] - mass*(cm(2)*cm(2)+cm(0)*cm(0));
918  prop.inertia.zz = intg[4] + intg[5] - mass*(cm(0)*cm(0)+cm(1)*cm(1));
919  prop.inertia.xy = (intg[7] - mass*cm(0)*cm(1));
920  prop.inertia.yz = (intg[8] - mass*cm(1)*cm(2));
921  prop.inertia.xz = (intg[9] - mass*cm(2)*cm(0));
922  if (mass != 0) {
923  prop.inertia.xx /= mass; prop.inertia.yy /= mass; prop.inertia.zz /= mass;
924  prop.inertia.xy /= mass; prop.inertia.yz /= mass; prop.inertia.xz /= mass;
925  }
926  return prop;
927  #undef VTB_GEOMETRY_SUBEXPRESSIONS
928 }
929 
930 template <class T, int N>
931 void
933  std::vector<Triangle<T,N>> triangles;
934  const auto& vertices = this->_vertices;
935  const auto& faces = this->_faces;
936  for (const auto& f : faces) {
937  const auto& v0 = vertices[f[0]];
938  const auto& v1 = vertices[f[1]];
939  const auto& v2 = vertices[f[2]];
940  triangles.emplace_back(v0,v1,v2);
941  }
942  std::sort(triangles.begin(), triangles.end(), CompareTriangles<T,N>{});
943  std::reverse(triangles.begin(), triangles.end());
944  for (const auto& t : triangles) {
945  const auto& v0 = t[0];
946  const auto& v1 = t[1];
947  const auto& v2 = t[2];
948  ::gnuplot(out, v0); out << '\n';
949  ::gnuplot(out, v1); out << "\n\n";
950  ::gnuplot(out, v2); out << '\n';
951  ::gnuplot(out, v2); out << "\n\n\n";
952  }
953 }
954 
955 template <class T, int N>
956 void
958  const auto& geometry = *this;
959  for (const auto& v : geometry.vertices()) {
960  out << "v " << v(0) << ' ' << v(1) << ' ' << v(2) << '\n';
961  }
962  for (const auto& v : geometry.vertex_normals()) {
963  out << "vn " << v(0) << ' ' << v(1) << ' ' << v(2) << '\n';
964  }
965  for (const auto& face : geometry.faces()) {
966  out << 'f';
967  for (const auto& idx : face) {
968  out << ' ' << (idx + 1);
969  }
970  out << '\n';
971  }
972 }
973 
975 
976 template <class T, int N>
978 vtb::geometry::operator<<(vtb::core::bstream& out, const Polyhedron<T,N>& rhs) {
979  out << rhs.vertices();
980  out << rhs.vertex_normals();
981  out << rhs.faces();
982  out << rhs.face_normals();
983  return out;
984 }
985 
986 template vtb::core::bstream&
987 vtb::geometry::operator<<(
988  vtb::core::bstream& out,
989  const Polyhedron<VTB_REAL_TYPE,3>& rhs
990 );
991 
992 template <class T, int N>
994 vtb::geometry::operator>>(vtb::core::bstream& in, Polyhedron<T,N>& rhs) {
995  in >> rhs._vertices;
996  in >> rhs._vertex_normals;
997  in >> rhs._faces;
998  in >> rhs._face_normals;
999  return in;
1000 }
1001 
1002 template vtb::core::bstream&
1003 vtb::geometry::operator>>(
1004  vtb::core::bstream& in,
1005  Polyhedron<VTB_REAL_TYPE,3>& rhs
1006 );
1007 
1008 template <class T,int N>
1009 T
1010 vtb::geometry::winding_number(const Polyhedron<T,N>& geometry, const Vertex<T,N>& origin) {
1011  const auto& v = geometry.vertices();
1012  const auto& faces = geometry.faces();
1013  T wn = 0;
1014  for (const auto& f : faces) {
1015  Tetrahedron<T,N> t{origin, v[f[0]], v[f[1]], v[f[2]]};
1016  wn += solid_angle(t);
1017  }
1018  return wn / vtb::base::constants<T>::pi(4);
1019 }
1020 
1021 template VTB_REAL_TYPE
1022 vtb::geometry::winding_number(const Polyhedron<VTB_REAL_TYPE,3>&, const Vertex<VTB_REAL_TYPE,3>&);
1023 
1024 template <class T, int N>
1025 auto
1026 vtb::geometry::inertia_tensor(const Polyhedron<T,N>& body) -> Inertia_tensor<T,N> {
1027  using std::abs;
1028  using blitz::pow2;
1029  Inertia_tensor<T,N> I;
1030  const auto& v = body.vertices();
1031  const auto& faces = body.faces();
1032  const auto& origin = centroid(body);
1033  T total_volume = 0;
1034  for (const auto& f : faces) {
1035  Tetrahedron<T,3> t{origin, v[f[0]], v[f[1]], v[f[2]]};
1036  auto t_signed_volume = signed_volume(t);
1037  auto t_volume = abs(t_signed_volume);
1038  #define x(i) t[i-1][0]
1039  #define y(i) t[i-1][1]
1040  #define z(i) t[i-1][2]
1041  I.x += t_volume*(
1042  pow2(y(1)) + y(1)*y(2) +
1043  pow2(y(2)) + y(1)*y(3) + y(2)*y(3) +
1044  pow2(y(3)) + y(1)*y(4) + y(2)*y(4) + y(3)*y(4) +
1045  pow2(y(4)) +
1046  pow2(z(1)) + z(1)*z(2) +
1047  pow2(z(2)) + z(1)*z(3) + z(2)*z(3) +
1048  pow2(z(3)) + z(1)*z(4) + z(2)*z(4) + z(3)*z(4) +
1049  pow2(z(4))) / 10;
1050  I.y += t_volume*(
1051  pow2(x(1)) + x(1)*x(2) +
1052  pow2(x(2)) + x(1)*x(3) + x(2)*x(3) +
1053  pow2(x(3)) + x(1)*x(4) + x(2)*x(4) + x(3)*x(4) +
1054  pow2(x(4)) +
1055  pow2(z(1)) + z(1)*z(2) +
1056  pow2(z(2)) + z(1)*z(3) + z(2)*z(3) +
1057  pow2(z(3)) + z(1)*z(4) + z(2)*z(4) + z(3)*z(4) +
1058  pow2(z(4))) / 10;
1059  I.z += t_volume*(
1060  pow2(x(1)) + x(1)*x(2) +
1061  pow2(x(2)) + x(1)*x(3) + x(2)*x(3) +
1062  pow2(x(3)) + x(1)*x(4) + x(2)*x(4) + x(3)*x(4) +
1063  pow2(x(4)) +
1064  pow2(y(1)) + y(1)*y(2) +
1065  pow2(y(2)) + y(1)*y(3) + y(2)*y(3) +
1066  pow2(y(3)) + y(1)*y(4) + y(2)*y(4) + y(3)*y(4) +
1067  pow2(y(4))) / 10;
1068  I.yz += t_volume*(
1069  2*y(1)*z(1) + y(2)*z(1) + y(3)*z(1) + y(4)*z(1) + y(1)*z(2) +
1070  2*y(2)*z(2) + y(3)*z(2) + y(4)*z(2) + y(1)*z(3) + y(2)*z(3) +
1071  2*y(3)*z(3) + y(4)*z(3) + y(1)*z(4) + y(2)*z(4) + y(3)*z(4) +
1072  2*y(4)*z(4)) / 20;
1073  I.xz += t_volume*(
1074  2*x(1)*z(1) + x(2)*z(1) + x(3)*z(1) + x(4)*z(1) + x(1)*z(2) +
1075  2*x(2)*z(2) + x(3)*z(2) + x(4)*z(2) + x(1)*z(3) + x(2)*z(3) +
1076  2*x(3)*z(3) + x(4)*z(3) + x(1)*z(4) + x(2)*z(4) + x(3)*z(4) +
1077  2*x(4)*z(4)) / 20;
1078  I.xy += t_volume*(
1079  2*x(1)*y(1) + x(2)*y(1) + x(3)*y(1) + x(4)*y(1) + x(1)*y(2) +
1080  2*x(2)*y(2) + x(3)*y(2) + x(4)*y(2) + x(1)*y(3) + x(2)*y(3) +
1081  2*x(3)*y(3) + x(4)*y(3) + x(1)*y(4) + x(2)*y(4) + x(3)*y(4) +
1082  2*x(4)*y(4)) / 20;
1083  #undef x
1084  #undef y
1085  #undef z
1086  total_volume += t_signed_volume;
1087  }
1088  if (total_volume != T{}) { I.scale(T{1}/total_volume); }
1089  return I;
1090 }
1091 
1092 template auto
1093 vtb::geometry::inertia_tensor(const Polyhedron<VTB_REAL_TYPE,3>& body) -> Inertia_tensor<VTB_REAL_TYPE,3>;
void flip(int dimension)
Flip the geometry over specified axis.
Mass_moments< T, N > mass_moments(const Polyline< T, N > &polyline)
Calculate area, centroid and inertia tensor.
void reorder()
Fix face indices winding order.
T distance(T... args)
Inertia_tensor< T, N > inertia_tensor(const Polyhedron< T, N > &body)
Calculate inertia tensor (matrix) of a body as a sum of inertia tensors for tetrahedrons.
T swap(T... args)
void normalise_faces()
Compute face normals only.
Binary space partitioning trees.
T end(T... args)
void move_to_centre_of_bounding_box()
Move origin to centre of bounding box.
void unique()
Remove duplicate vertices and faces.
void scale(const vertex_type &ratio)
Scale the geometry by specified ratio over each dimension.
T min(T... args)
T lowest(T... args)
Estimates sample mean without overflows.
Definition: mean.hh:20
static constexpr const T pi()
Definition: constants.hh:18
T max(T... args)
void move_to_centre_of_mass()
Move origin to centre of mass.
Three-dimensional polyhedron.
Definition: polyhedron.hh:43
void normalise()
Compute face and vertex normals in one loop.
T rotate(T... args)
void wall(scalar_type thickness)
Shrink or extend the geometry by thickness in the direction of centroid.
void mirror(int dimension)
Extend the geometry by mirroring it over the specified axis.
void merge(const Polyhedron &rhs)
Add all vertices, faces and normals from rhs.
void rotate(int dimension, int degrees)
Rotate the geometry by angle which is multiple of 90 degrees.