FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
surface_mesh.hpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#pragma once
7
8#include <kernel/geometry/atlas/chart.hpp>
9#include <kernel/geometry/conformal_mesh.hpp>
10#include <kernel/geometry/index_calculator.hpp>
11
12#include <kernel/util/math.hpp>
13
14#include <deque>
15#include <memory>
16
17namespace FEAT
18{
19 namespace Geometry
20 {
21 namespace Atlas
22 {
23
28 {
30 static constexpr bool is_explicit = false;
32 static constexpr bool is_implicit = true;
34 static constexpr int world_dim = 3;
36 static constexpr int param_dim = 2;
37 }; // struct SurfaceMeshTraits
38
49 template<typename Mesh_>
51 public ChartCRTP<SurfaceMesh<Mesh_>, Mesh_, SurfaceMeshTraits>
52 {
53 public:
65 std::unique_ptr<SurfaceMeshType> _surface_mesh;
66
67 private:
69 // For computing point projections, we need to know if an edge was already traversed by adding the neighbor
70 // across said edge to the search stack. If the surface is concave, it might happen that triangle A says
71 // "go to my neighbor, B", and then B says "go to my neighbor, A". It's then clear that the wanted point
72 // lies on the edge between A and B.
76
77 public:
79 SurfaceMesh() = delete;
80
84 explicit SurfaceMesh(std::unique_ptr<SurfaceMeshType> surf_mesh) :
85 _surface_mesh(std::move(surf_mesh)),
86 _edge_marker(new bool[_surface_mesh->get_num_entities(SurfaceMeshType::shape_dim-1)]),
87 _cell_marker(new bool[_surface_mesh->get_num_entities(SurfaceMeshType::shape_dim)])
88 {
89 }
90
93
97 virtual ~SurfaceMesh()
98 {
99 delete[] _edge_marker;
100 delete[] _cell_marker;
101 }
102
104 virtual std::size_t bytes() const override
105 {
106 if(_surface_mesh != nullptr)
107 return _surface_mesh->bytes();
108 else
109 return std::size_t(0);
110 }
111
113 virtual String get_type() const override
114 {
115 return "SurfaceMesh";
116 }
117
119 virtual void transform(const WorldPoint& origin, const WorldPoint& angles, const WorldPoint& offset) override
120 {
121 // create rotation matrix
123 rot.set_rotation_3d(angles[0], angles[1], angles[2]);
124
125 // transform all world points
126 WorldPoint tmp;
127 auto& vtx = _surface_mesh->get_vertex_set();
128 for(Index i(0); i < vtx.get_num_vertices(); ++i)
129 {
130 tmp = vtx[i] - origin;
131 vtx[i].set_mat_vec_mult(rot, tmp) += offset;
132 }
133 }
134
136 virtual void write(std::ostream& os, const String& sindent) const override
137 {
138 String indent(sindent);
139
140 os << indent << "<SurfaceMesh";
141 os << " verts=\" " << _surface_mesh->get_num_entities(0) << "\"";
142 os << " trias=\" " << _surface_mesh->get_num_entities(ShapeType::dimension) << "\"";
143 os << ">\n";
144
145 // increase indent
146 indent.resize(indent.size()+2, ' ');
147
148 const auto& vtx = _surface_mesh->get_vertex_set();
149
150 os << indent << "<Vertices>\n";
151 indent.resize(indent.size()+2, ' ');
152
153 for(Index i(0); i < vtx.get_num_vertices(); ++i)
154 {
155 const auto& v = vtx[i];
156 os << indent << v[0];
157 for(int j(1); j < SurfaceMeshType::world_dim; ++j)
158 os << ' ' << v[j];
159 os << '\n';
160 }
161
162 indent.resize(indent.size()-2);
163 os << indent << "</Vertices>\n";
164
165 // Get vertex at cell index set
166 const auto& idx = _surface_mesh->template get_index_set<ShapeType::dimension, 0>();
167
168 os << indent << "<Triangles>\n";
169 indent.resize(indent.size()+2, ' ');
170
171 for(Index i(0); i < idx.get_num_entities(); ++i)
172 {
173 const auto& idx_loc = idx[i];
174 os << indent << idx_loc[0];
175 for(int j(1); j < idx.num_indices; ++j)
176 os << ' ' << idx_loc[j];
177 os << '\n';
178 }
179
180 indent.resize(indent.size()-2);
181 os << indent << "</Triangles>\n";
182
183 indent.resize(indent.size()-2);
184 os << indent << "</SurfaceMesh>";
185 }
186
193 void project_point(WorldPoint& point) const
194 {
195 CoordType signed_distance(0);
196 WorldPoint grad_distance(CoordType(0));
197
198 project_point(point, signed_distance, grad_distance);
199 }
200
213 void project_point(WorldPoint& point, CoordType& signed_distance, WorldPoint& grad_distance) const
214 {
215 // There is nothing to do if the surface mesh does not have any cells
216 if(_surface_mesh->get_num_entities(SurfaceMeshType::shape_dim) == Index(0))
217 return;
218
219 // Vertex at cell information
220 const auto& idx(_surface_mesh->template get_index_set<SurfaceMeshType::shape_dim, 0>());
221 // The mesh's vertex set so we can get at the coordinates
222 const auto& vtx(_surface_mesh->get_vertex_set());
223
224 // When a point is found, this will hold the coefficients of its image under the projection wrt. the
225 // standard P1/Q1 transformation
227 // This will hold the coefficients of the transformation mapping
229
230 // Since we want to project the point an arbitrary distance, we set this to something huge
231 CoordType acceptable_distance(Math::huge<CoordType>());
232 // Find the facet in the SurfaceMesh we project to and compute the coefficients
233 Index best_facet =
234 find_cell(coeffs, point, Index(0), acceptable_distance, *_surface_mesh, true);
235
236 // Clip the coefficients
237 for(int j(0); j < SurfaceMeshType::shape_dim; ++j)
238 {
239 coeffs[j] = Math::max(coeffs[j], CoordType(0));
240 coeffs[j] = Math::min(coeffs[j], CoordType(1));
241 }
242 // Now that we have the surface mesh cell with the lowest distance, we can compute the projected point
243 WorldPoint projected_point(CoordType(0));
244
245 // Evaluate the surface mesh trafo for the computed coefficients
246 CoordType coeff0(CoordType(1));
247 for(int j(0); j < SurfaceMeshType::shape_dim; ++j)
248 {
249 // Index of the local vertex i in the SurfaceMesh
250 Index i(idx(best_facet, j+1));
251 projected_point += coeffs[j]*vtx[i];
252 coeff0 -= coeffs[j];
253 }
254
255 projected_point += coeff0*vtx[idx(best_facet, Index(0))];
256
257 grad_distance = (projected_point - point);
258 signed_distance = grad_distance.norm_euclid();
259
260 // If the distance is too small, we set the gradient vector to zero
261 if(signed_distance < Math::eps<CoordType>())
262 grad_distance.format(CoordType(0));
263 else
264 {
265 grad_distance.normalize();
266 WorldPoint nu(get_normal_on_tria(best_facet, coeffs));
267 signed_distance *= Math::signum(Tiny::dot(nu, grad_distance));
268 }
269
270 point = projected_point;
271
272 }
273
287 const Index facet, const Tiny::Vector<CoordType, SurfaceMeshType::shape_dim+1> DOXY(coeffs)) const
288 {
289 // Vertex at cell information
290 const auto& idx(_surface_mesh->template get_index_set<SurfaceMeshType::shape_dim, 0>());
291 // The mesh's vertex set so we can get at the coordinates
292 const auto& vtx(_surface_mesh->get_vertex_set());
293
296
297 Index i0(idx(facet, 0));
298 Index i1(idx(facet, 1));
299 Index i2(idx(facet, 2));
300
301 coords[0] = vtx[i1] - vtx[i0];
302 coords[1] = vtx[i2] - vtx[i0];
303
304 for(int i(0); i < coords.m; ++i)
305 {
306 for(int j(0); j < coords.n; ++j)
307 coords_transpose(j,i) = coords(i,j);
308 }
309
310 WorldPoint nu(Tiny::orthogonal(coords_transpose));
311 nu.normalize();
312
313 return nu;
314 }
315
330 void project_meshpart(Mesh_& mesh, const MeshPart<Mesh_>& meshpart) const
331 {
332 // We need the topology of the meshpart because searching for points is cell-wise and we need to know
333 // about the vertices in each cell
334 XASSERTM(meshpart.get_topology() != nullptr, "The meshpart needs a topology for SurfaceMesh::project()!");
335
337
338 // There is nothing to do if the meshpart does not have any cells (which refer to facets of the original
339 // mesh)
340 if(num_facets == Index(0))
341 return;
342
343 // The number of vertices that need to be projected
344 const Index num_verts(meshpart.get_num_entities(0));
345 // Mapping of vertices from the meshpart to the real mesh
346 const auto& ts_verts(meshpart.template get_target_set<0>());
347
348 // Vertex at surface mesh cell information
349 const auto& idx_sm(_surface_mesh->template get_index_set<SurfaceMeshType::shape_dim, 0>());
350 // Vertex set of the surface mesh
351 const auto& vtx_sm(_surface_mesh->get_vertex_set());
352
353 // Vertex at facet information for the meshpart
354 const auto& idx_mp(meshpart.template get_index_set<SurfaceMeshType::shape_dim, 0>());
355 // Subfacet at facet information for the meshpart. i.e. if Mesh is a Hypercube<3> mesh, the boundary is a
356 // Hypercube<2> mesh, the facets are quads and THEIR subfacets are edges, so this is edge@quad
357 // Needed for computing neighbors.
358 const auto& facet_idx_mp(meshpart.template get_index_set
360
361 // Neighbor information for all facets in the MeshPart. Needed for adding facets to the search stack
362 // so we can exploit local information
365
366 Geometry::Intern::FacetNeighbors::compute(neigh_mp, facet_idx_mp);
367
368 // The mesh's vertex set, since we modify the coordinates by projection
369 auto& vtx(mesh.get_vertex_set());
370
371 // When a point is found, this will hold the coefficients of its image under the projection wrt. the
372 // standard P1/Q1 transformation
374
375 // For every vertex, we need to know if it was already projected
376 bool* vert_todo(new bool[num_verts]);
377 for(Index i(0); i < num_verts; ++i)
378 vert_todo[i] = true;
379
380 // For every facet, we need to know if it was already added to the search stack
381 bool* facet_todo(new bool[num_facets]);
382 for(Index i(0); i < num_facets; ++i)
383 facet_todo[i] = true;
384
385 // For every facet we keep a guess, a cell number in the surface mesh where we start looking for vertices
386 // from our facet.
387 Index* guesses(new Index[num_facets]);
388 for(Index i(0); i < num_facets; ++i)
389 guesses[i] = Index(0);
390
391 // The search stack for all facets. We add neighbors to it to keep the search as local as possible
392 std::deque<Index> facet_stack;
393 facet_stack.push_front(0);
394
395 // Treat all facets
396 while(!facet_stack.empty())
397 {
398 // This is the facet from the mesh part that's being worked on
399 Index current_facet(facet_stack.front());
400 // This will be the facet from the surface mesh were the vertex was found, set it to the best guess
401 // initially
402 Index facet_sm(guesses[current_facet]);
403
404 facet_stack.pop_front();
405 facet_todo[current_facet] = false;
406
407 // We want to orthogonally project a point, so we need to figure out an acceptable distance for this
408 // projection. Otherwise, it might happen that we project to the far side of the domain
409 CoordType acceptable_distance(CoordType(1));
410
411 // Compute acceptable distance. Because we do not have the transformation available, we cannot compute
412 // i.e. the volume of the facet to deduce an acceptable distance from that. So instead, we pick a
413 // random vertex of the facet, compute the distances to all other points, and take the geometric mean.
414 for(int j(0); j < idx_mp.num_indices-1; ++j)
415 {
416 Index i0(idx_mp(current_facet, 0));
417 Index i_mp(idx_mp(current_facet, j+1));
418 acceptable_distance *= ((vtx[i_mp] - vtx[i0]).norm_euclid());
419 }
420 acceptable_distance = Math::pow(
421 acceptable_distance, CoordType(1)/CoordType(idx_mp.num_indices) - CoordType(1));
422
423 // Find all vertices in the current facet
424 for(int j(0); j < idx_mp.num_indices; ++j)
425 {
426 // The index of the local vertex j in the Meshpart
427 Index i_mp(idx_mp(current_facet, j));
428
429 if(vert_todo[i_mp])
430 {
431 vert_todo[i_mp] = false;
432 const auto& x(vtx[ts_verts[i_mp]]);
433
434 // Find the facet in the SurfaceMesh we project to and compute the coefficients. We break at the
435 // first facet fulfilling the criteria
436 facet_sm = find_cell(coeffs, x, guesses[current_facet], acceptable_distance, * _surface_mesh, false);
437 // Update the guess for this facet because there might be more points to project
438 guesses[current_facet] = facet_sm;
439
440 // This is the index of the local vertex j in the real mesh
441 Index i_parent(ts_verts[i_mp]);
442 // Clear the old vertex coordinates so we can just add onto that
443 vtx[i_parent].format(CoordType(0));
444 // Evaluate the surface mesh trafo for the computed coefficients
445 CoordType coeff0(CoordType(1));
446 for(int i(0); i < SurfaceMeshType::shape_dim; ++i)
447 {
448 // Index of the local vertex i in the SurfaceMesh
449 Index i_sm(idx_sm(facet_sm, i+1));
450 vtx[i_parent] += coeffs[i]*vtx_sm[i_sm];
451 coeff0 -= coeffs[i];
452 }
453 vtx[i_parent] += coeff0*vtx_sm[idx_sm(facet_sm, 0)];
454
455 } // vert_todo[i_mp]
456 } // vertices in current facet
457
458 // Now add all neighbors that are still on the todo list to the search stack
459 for(int l(0); l < neigh_mp.num_indices; ++l)
460 {
461 Index k(neigh_mp(current_facet, l));
462 if(k != ~Index(0) && facet_todo[k])
463 {
464 facet_todo[k] = false;
465 facet_stack.push_back(k);
466 // The last vertex was found in facet_sm, and since k is a neighbor of current, it cannot be too
467 // far off
468 guesses[k] = facet_sm;
469 }
470 } // adding neighbors
471
472 } // search stack
473
474 // Clean up
475 delete[] facet_todo;
476 delete[] vert_todo;
477 delete[] guesses;
478 }
479
482 {
483 WorldPoint grad_distance(CoordType(0));
484 return compute_dist(point, grad_distance);
485 }
486
488 CoordType compute_dist(const WorldPoint& point, WorldPoint& grad_distance) const
489 {
490 CoordType signed_distance(0);
491 WorldPoint projected_point(point);
492 project_point(projected_point, signed_distance, grad_distance);
493
494 return Math::abs(signed_distance);
495 }
496
499 {
500 WorldPoint grad_distance(CoordType(0));
501 return compute_signed_dist(point, grad_distance);
502 }
503
505 CoordType compute_signed_dist(const WorldPoint& point, WorldPoint& grad_distance) const
506 {
507 CoordType signed_distance(0);
508 WorldPoint projected_point(point);
509 project_point(projected_point, signed_distance, grad_distance);
510
511 return signed_distance;
512 }
513
514 private:
551 template<int world_dim, int sc_, int sx_>
554 const Tiny::Vector<CoordType, world_dim, sx_>& x, Index guess, CoordType acceptable_distance,
555 const SurfaceMeshType& mesh, bool find_best_approx) const
556 {
557 // For every subfacet, we need to know if it was already traversed by adding the neighbor across said
558 // facet to the search stack. If the surface is concave, it might happen that facet A says "go to my
559 // neighbor, B", and then B says "go to my neighbor, A". It's then clear that the wanted point lies on
560 // the facet between A and B.
561
562 // Clear traversed information
563 for(Index facet(0); facet < mesh.get_num_entities(SurfaceMeshType::shape_dim-1); ++facet)
564 _edge_marker[facet] = false;
565
567 // Clear cell todo information
568 for(Index cell(0); cell < num_cells; ++cell)
569 _cell_marker[cell] = false;
570
571 // Type of the vertex at cell index set
572 typedef typename SurfaceMeshType::template IndexSet<SurfaceMeshType::shape_dim, 0>::Type VertAtCellIdxType;
573 // Number of vertices per cell
574 static constexpr int num_vert_loc = VertAtCellIdxType::num_indices;
575 // Vertex at cell information
576 const VertAtCellIdxType& idx(mesh.template get_index_set<SurfaceMeshType::shape_dim, 0>());
577 // Facet at cell information
578 const auto& facet_idx(mesh.template
579 get_index_set<SurfaceMeshType::shape_dim, SurfaceMeshType::shape_dim-1>());
580 // The mesh's vertex set so we can get at the coordinates
581 const auto& vtx(mesh.get_vertex_set());
582
583 const auto& neigh(mesh.get_neighbors());
584
585 // This will contain all vertices making up a cell in the mesh. Since the mesh is of shape
586 // Simplex<shape_dim>, there are shape_dim+1 vertices.
588
589 // This is the search stack
590 std::deque<Index> cell_stack;
591 // We start with our guess
592 cell_stack.push_front(guess);
593
594
595 Index best_approx_cell(~Index(0));
596
597 CoordType best_approx_dist(Math::huge<CoordType>());
598
600
601 // Search all cells
602 while(!cell_stack.empty())
603 {
604 // Get new cell
605 Index current(cell_stack.front());
606 cell_stack.pop_front();
607 _cell_marker[current] = true;
608
609 // Collect all vertices that make up the cell in the coords matrix
610 for(int j(0); j < SurfaceMeshType::shape_dim+1; ++j)
611 {
612 Index i(idx(current, j));
613 coords[j] = vtx[i];
614 }
615
616 // Compute the coefficients of the inverse coordinate transformation. The last coefficient is the
617 // signed distance of x to the facet made up by coords
618 compute_inverse_coeffs(current_coeffs, x, coords);
619
620 // From the coefficients, we can compute the barycentric coordinates (as the surface mesh is of Simplex
621 // shape) so we get an idea about in which direction we need to search first
623 CoordType ortho_dist = compute_bary_and_dist(bary, current_coeffs);
624
625 // From all this, we can now determine if there is an orthogonal projection of x onto this facet. For
626 // this, we need to know the traversed information as well and the neighbor information of the current
627 // facet
628 bool success = determine_success(ortho_dist, acceptable_distance, bary, facet_idx[current], _edge_marker);
629
630 // Ok, maybe we are done
631 if(success && !find_best_approx)
632 {
633 best_approx_cell = current;
634 coeffs = current_coeffs;
635 break;
636 }
637 // Check if we need to update the best approx data
638 else if(success && find_best_approx)
639 {
640 if(ortho_dist < best_approx_dist)
641 {
642 best_approx_dist = ortho_dist;
643 best_approx_cell = current;
644 coeffs = current_coeffs;
645
646 }
647 }
648
649 // If we are not done, add all neighbors that have not been searched to the search stack
650 for(int l(0); l < SurfaceMeshType::shape_dim+1; ++l)
651 {
652 // The neighbor over the l-th facet
653 Index n(neigh(current, l));
654 // Check if the neighbor exists and is still on the to do list
655 if(n != ~ Index(0) && !_cell_marker[n])
656 {
657 Index facet(facet_idx(current,l));
658 // If the neighbor is still on the to do list and the facet it lies at is in the traversed list,
659 // this means the barycentric coordinate for the corresponding direction was negative, meaning
660 // that x lies in that direction. So we add that neighbor to the front.
661 if(_edge_marker[facet])
662 cell_stack.push_front(n);
663 else
664 cell_stack.push_back(n);
665
666 // Since we added the neighbor to the stack, prevent it from being added again later.
667 _cell_marker[n] = true;
668 }
669 } // adding neighbors
670
671 } // search loop
672
673 // If we get here without success being true, the point could not be found
674 XASSERTM(best_approx_cell == ~Index(0), "Could not find point "+stringify(x)+" in SurfaceMesh");
675
676 // Otherwise, it was found in current and coeffs is still set from that evaluation
677 return best_approx_cell;
678 } // find_cell()
679
730 template<typename DT_, int n_, int sn_, int sb_>
732 {
733 bary(0) = CoordType(1);
734 for(int j(0); j < n_-1; ++j)
735 {
736 bary(0) -= coeffs(j);
737 bary(j+1) = coeffs(j);
738 }
739
740 return Math::abs(coeffs(n_-1));
741 }
742
807 template<typename DT_, int n_, int sn_, typename IdxType_>
808 static bool determine_success(
809 DT_ ortho_dist,
810 DT_ acceptable_distance,
812 const IdxType_& facet_idx,
813 bool* traversed)
814 {
815 static const DT_ tol = Math::sqrt(Math::eps<DT_>());
816 bool success(true);
817
818 // Determine success
819 if(ortho_dist > acceptable_distance)
820 success = false;
821
822 int num_negative(0);
823
824 for(int l(0); l < n_; ++l)
825 {
826 if(bary(l) < - tol)
827 {
828 Index facet(facet_idx[l]);
829 ++num_negative;
830 if(traversed[facet])
831 {
832 bary(l) = CoordType(0);
833 }
834 else
835 {
836 success = false;
837 traversed[facet] = true;
838 }
839 }
840 }
841
842 if(num_negative > 1)
843 success = false;
844
845 return success;
846 }
847
849
854 template<typename DT_, int sb_, int sp_, int smx_, int snx_>
855 void compute_inverse_coeffs(
857 const Tiny::Vector<DT_, 3, sp_>& point,
859 {
860 coeffs.format(DT_(0));
861
862 // A will contain the transformation matrix
863 Tiny::Matrix<DT_, 3, 3> A(DT_(0));
864 // Fill the all rows in the first shape_dim = world_dim-1 columns
865 for(int i(0); i < 3; ++i)
866 {
867 for(int j(0); j < 2; ++j)
868 A(i,j) = x(j+1,i) - x(0,i);
869 }
870
871 // The last column is the additional direction for our augmented simplex and it is orthogonal to the rest
873 Tiny::orthogonal_3x2(ortho, A);
874 // Normalize this so the last coefficient is the signed distance
875 ortho.normalize();
876
877 // Set the last column in A
878 for(int i(0); i < 3; ++i)
879 A(i,2) = ortho(i);
880
881 // This will be the inverse of the transformation matrix
882 Tiny::Matrix<DT_, 3 , 3> Ainv(DT_(0));
883 Ainv.set_inverse(A);
884
885 // This is the solution of A u = point - x[0]
886 coeffs = Ainv*(point - x[0]);
887 }
888
894 template<typename DT_, int world_dim, int sc_, int sp_, int smx_, int snx_>
895 void compute_inverse_coeffs(
899 {
900 static_assert( (world_dim == 2 || world_dim == 3),
901 "world dim has to be 2 or 3 for complementary barycentric coordinates");
902
903 auto tmp = x[1]-x[0];
904 DT_ sp(Tiny::dot(point - x[0],tmp));
905 DT_ nsqr(Math::sqr(tmp.norm_euclid()));
906 coeffs[0] = sp/nsqr;
907 tmp = point - (x[0] + coeffs[0]*(x[1]-x[0]));
908 coeffs[1] = tmp.norm_euclid();
909 }
911 }; // class SurfaceMesh
912
914 template<typename Mesh_>
915 class SurfaceMeshVertsParser :
916 public Xml::MarkupParser
917 {
918 public:
919 typedef typename Mesh_::CoordType CoordType;
920 typedef VertexSet<3, CoordType> VertexSetType;
921
922 protected:
923 VertexSetType& _vertex_set;
924 Index _read;
925
926 public:
927 explicit SurfaceMeshVertsParser(VertexSetType& vertex_set) :
928 _vertex_set(vertex_set),
929 _read(0)
930 {
931 }
932
933 virtual bool attribs(std::map<String,bool>&) const override
934 {
935 return true;
936 }
937
938 virtual void create(int iline, const String& sline, const String&, const std::map<String, String>&, bool closed) override
939 {
940 if(closed)
941 throw Xml::GrammarError(iline, sline, "Invalid closed markup");
942 }
943
944 virtual void close(int iline, const String& sline) override
945 {
946 // ensure that we have read all vertices
947 if(_read < _vertex_set.get_num_vertices())
948 throw Xml::GrammarError(iline, sline, "Invalid terminator; expected point");
949 }
950
951 virtual std::shared_ptr<MarkupParser> markup(int, const String&, const String&) override
952 {
953 // no children allowed
954 return nullptr;
955 }
956
957 virtual bool content(int iline, const String& sline) override
958 {
959 // make sure that we do not read more points than expected
960 if(_read >= _vertex_set.get_num_vertices())
961 throw Xml::ContentError(iline, sline, "Invalid content; expected terminator");
962
963 // split line by whitespaces
964 std::deque<String> scoords = sline.split_by_whitespaces();
965
966 // check size
967 if(scoords.size() != std::size_t(3))
968 throw Xml::ContentError(iline, sline, "Invalid number of coordinates");
969
970 // get our current vertex
971 auto& vtx = _vertex_set[_read];
972 for(int i(0); i < 3; ++i)
973 {
974 if(!scoords.at(std::size_t(i)).parse(vtx[i]))
975 throw Xml::ContentError(iline, sline, "Failed to parse vertex coordinate");
976 }
977
978 // okay, another point done
979 ++_read;
980
981 return true;
982 }
983 };
984
985 template<typename Mesh_>
986 class SurfaceMeshTriasParser :
987 public Xml::MarkupParser
988 {
989 public:
990 typedef IndexSet<3> IndexSetType;
991
992 protected:
993 IndexSetType& _index_set;
994 Index _read;
995
996 public:
997 explicit SurfaceMeshTriasParser(IndexSetType& index_set) :
998 _index_set(index_set),
999 _read(0)
1000 {
1001 }
1002
1003 virtual bool attribs(std::map<String,bool>&) const override
1004 {
1005 return true;
1006 }
1007
1008 virtual void create(int iline, const String& sline, const String&, const std::map<String, String>&, bool closed) override
1009 {
1010 if(closed)
1011 throw Xml::GrammarError(iline, sline, "Invalid closed markup");
1012 }
1013
1014 virtual void close(int iline, const String& sline) override
1015 {
1016 // ensure that we have read all triangles
1017 if(_read < _index_set.get_num_entities())
1018 throw Xml::GrammarError(iline, sline, "Invalid terminator; expected triangle indices");
1019 }
1020
1021 virtual std::shared_ptr<MarkupParser> markup(int, const String&, const String&) override
1022 {
1023 // no children allowed
1024 return nullptr;
1025 }
1026
1027 virtual bool content(int iline, const String& sline) override
1028 {
1029 // make sure that we do not read more points than expected
1030 if(_read >= _index_set.get_num_entities())
1031 throw Xml::ContentError(iline, sline, "Invalid content; exprected terminator");
1032
1033 // split line by whitespaces
1034 std::deque<String> sidx = sline.split_by_whitespaces();
1035
1036 // check size
1037 if(sidx.size() != std::size_t(3))
1038 throw Xml::ContentError(iline, sline, "Invalid number of indices");
1039
1040 // get our current vertex
1041 auto& idx = _index_set[_read];
1042 for(int i(0); i < 3; ++i)
1043 {
1044 if(!sidx.at(std::size_t(i)).parse(idx[i]))
1045 throw Xml::ContentError(iline, sline, "Failed to parse vertex index");
1046 }
1047
1048 // okay, another point done
1049 ++_read;
1050
1051 return true;
1052 }
1053 };
1054
1055 template<typename Mesh_, bool enable_ = (Mesh_::shape_dim > 2)>
1056 class SurfaceMeshChartParser :
1057 public Xml::DummyParser
1058 {
1059 public:
1060 explicit SurfaceMeshChartParser(std::unique_ptr<ChartBase<Mesh_>>&)
1061 {
1062 XABORTM("Thou shall not arrive here");
1063 }
1064 };
1065
1066 template<typename Mesh_>
1067 class SurfaceMeshChartParser<Mesh_, true> :
1068 public Xml::MarkupParser
1069 {
1070 protected:
1071 typedef typename SurfaceMesh<Mesh_>::SurfaceMeshType SurfaceMeshType;
1072 std::unique_ptr<ChartBase<Mesh_>>& _chart;
1073 std::unique_ptr<SurfaceMeshType> _surfmesh;
1074 Index _nverts, _ntrias;
1075 bool _have_verts, _have_trias;
1076
1077 public:
1078 explicit SurfaceMeshChartParser(std::unique_ptr<ChartBase<Mesh_>>& chart) :
1079 _chart(chart),
1080 _surfmesh(),
1081 _nverts(0),
1082 _ntrias(0),
1083 _have_verts(false), _have_trias(false)
1084 {
1085 }
1086
1087 virtual bool attribs(std::map<String,bool>& attrs) const override
1088 {
1089 attrs.emplace("verts", true);
1090 attrs.emplace("trias", true);
1091 return true;
1092 }
1093
1094 virtual void create(
1095 int iline,
1096 const String& sline,
1097 const String&,
1098 const std::map<String, String>& attrs,
1099 bool closed) override
1100 {
1101 // make sure this one isn't closed
1102 if(closed)
1103 throw Xml::GrammarError(iline, sline, "Invalid closed SurfaceMesh markup");
1104
1105 // try to parse the size
1106 if(!attrs.find("verts")->second.parse(_nverts))
1107 throw Xml::GrammarError(iline, sline, "Failed to parse number of vertices");
1108 if(!attrs.find("trias")->second.parse(_ntrias))
1109 throw Xml::GrammarError(iline, sline, "Failed to parse number of triangles");
1110
1111 // create surface mesh
1112 Index num_entities[3] = {_nverts, 0, _ntrias};
1113 _surfmesh.reset(new SurfaceMeshType(num_entities));
1114 }
1115
1116 virtual void close(int iline, const String& sline) override
1117 {
1118 if(!_have_verts)
1119 throw Xml::GrammarError(iline, sline, "Vertices of SurfaceMesh are missing");
1120 if(!_have_trias)
1121 throw Xml::GrammarError(iline, sline, "Triangles of SurfaceMesh are missing");
1122
1123 // deduct topology
1124 _surfmesh->deduct_topology_from_top();
1125
1126 // note: '_surfmesh' is a Geometry::ConformalMesh, not a Chart::SurfaceMesh
1127 _chart.reset(new SurfaceMesh<Mesh_>(std::move(_surfmesh)));
1128 }
1129
1130 virtual bool content(int, const String&) override
1131 {
1132 return false;
1133 }
1134
1135 virtual std::shared_ptr<Xml::MarkupParser> markup(int, const String&, const String& name) override
1136 {
1137 if(name == "Vertices")
1138 {
1139 _have_verts = true;
1140 return std::make_shared<SurfaceMeshVertsParser<Mesh_>>(_surfmesh->get_vertex_set());
1141 }
1142 if(name == "Triangles")
1143 {
1144 _have_trias = true;
1145 return std::make_shared<SurfaceMeshTriasParser<Mesh_>>(_surfmesh->template get_index_set<2,0>());
1146 }
1147 return nullptr;
1148 }
1149 };
1151
1152 } // namespace Atlas
1153 } // namespace Geometry
1154} // namespace FEAT
#define XABORTM(msg)
Abortion macro definition with custom message.
Definition: assertion.hpp:192
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
Chart CRTP base-class template.
Definition: chart.hpp:354
static constexpr int world_dim
the world dimension of this chart
Definition: chart.hpp:377
Boundary description by a surface mesh in 3d.
SurfaceMesh()=delete
Explicitly delete empty default constructor.
CoordType compute_dist(const WorldPoint &point, WorldPoint &grad_distance) const
Computes the distance of a point to this chart.
void project_meshpart(Mesh_ &mesh, const MeshPart< Mesh_ > &meshpart) const
Orthogonally projects all vertices at facets of a MeshPart.
virtual void transform(const WorldPoint &origin, const WorldPoint &angles, const WorldPoint &offset) override
std::unique_ptr< SurfaceMeshType > _surface_mesh
Pointer to the surface mesh object.
bool * _edge_marker
Marker for edges.
virtual void write(std::ostream &os, const String &sindent) const override
Writes the Chart into a stream in XML format.
WorldPoint get_normal_on_tria(const Index facet, const Tiny::Vector< CoordType, SurfaceMeshType::shape_dim+1 > coeffs) const
Computes the outer normal on a triangle for a single point.
virtual ~SurfaceMesh()
Virtual destructor.
BaseClass::CoordType CoordType
Floating point type for coordinates.
virtual std::size_t bytes() const override
SurfaceMesh(std::unique_ptr< SurfaceMeshType > surf_mesh)
Constructor getting an object.
ConformalMesh< ShapeType, 3, CoordType > SurfaceMeshType
The type for the mesh defining the discrete surface.
void project_point(WorldPoint &point, CoordType &signed_distance, WorldPoint &grad_distance) const
Projects a single point to the surface given by the surface mesh.
Shape::Simplex< 2 > ShapeType
The shape type is always Simplex<2>
BaseClass::WorldPoint WorldPoint
Vector type for world points.
Index find_cell(Tiny::Vector< CoordType, SurfaceMeshType::shape_dim+1, sc_ > &coeffs, const Tiny::Vector< CoordType, world_dim, sx_ > &x, Index guess, CoordType acceptable_distance, const SurfaceMeshType &mesh, bool find_best_approx) const
Finds the cell in the surface mesh that contains a specific point.
virtual String get_type() const override
Writes the type as String.
bool * _cell_marker
Marker for triangles.
static DT_ compute_bary_and_dist(Tiny::Vector< DT_, n_, sb_ > &bary, const Tiny::Vector< DT_, n_, sn_ > &coeffs)
For given reference cell coordinates, this computes the barycentric coordinates and the distance.
static bool determine_success(DT_ ortho_dist, DT_ acceptable_distance, Tiny::Vector< DT_, n_, sn_ > &bary, const IdxType_ &facet_idx, bool *traversed)
Determines the success of the point search.
CoordType compute_signed_dist(const WorldPoint &point) const
Computes the signed distance of a point to this chart.
CoordType compute_signed_dist(const WorldPoint &point, WorldPoint &grad_distance) const
Computes the signed distance of a point to this chart.
CoordType compute_dist(const WorldPoint &point) const
Computes the distance of a point to this chart.
void project_point(WorldPoint &point) const
Projects a single point to the surface given by the surface mesh.
ChartCRTP< SurfaceMesh< Mesh_ >, Mesh_, SurfaceMeshTraits > BaseClass
The CRTP base class.
SurfaceMesh(SurfaceMeshType &&)=delete
Explicitly delete move constructor.
Conformal mesh class template.
Index get_num_entities(int dim) const
Returns the number of entities.
static constexpr int shape_dim
shape dimension
IndexSet< shape_dim, shape_dim-1 >::Type & get_neighbors()
static constexpr int world_dim
world dimension
VertexSetType & get_vertex_set()
Returns a reference to the vertex set of the mesh.
Conformal Index-Set class template.
Definition: index_set.hpp:82
Class template for partial meshes.
Definition: mesh_part.hpp:90
Index get_num_entities(int dim) const
Returns the number of entities.
Definition: mesh_part.hpp:311
String class implementation.
Definition: string.hpp:46
Tiny Matrix class template.
static constexpr int n
the column count of the matrix
static constexpr int m
the row count of the matrix
CUDA_HOST_DEVICE Matrix & set_rotation_3d(T_ yaw, T_ pitch, T_ roll)
Sets this matrix to a 3D yaw-pitch-roll rotation matrix.
Tiny Vector class template.
CUDA_HOST_DEVICE void format(DataType alpha=DataType(0))
Formats the vector.
CUDA_HOST_DEVICE Vector & normalize()
Normalizes this vector.
CUDA_HOST_DEVICE DataType norm_euclid() const
Computes the euclid norm of this vector.
T_ sqrt(T_ x)
Returns the square-root of a value.
Definition: math.hpp:300
T_ abs(T_ x)
Returns the absolute value.
Definition: math.hpp:275
T_ pow(T_ x, T_ y)
Returns x raised to the power of y.
Definition: math.hpp:643
T_ sqr(T_ x)
Returns the square of a value.
Definition: math.hpp:95
T_ min(T_ a, T_ b)
Returns the minimum of two values.
Definition: math.hpp:123
T_ signum(T_ x)
Returns the sign of a value.
Definition: math.hpp:250
T_ max(T_ a, T_ b)
Returns the maximum of two values.
Definition: math.hpp:137
CUDA_HOST_DEVICE T_ dot(const T_ &a, const T_ &b)
Computes the dot-product of two scalars.
CUDA_HOST_DEVICE void orthogonal_3x2(Vector< T_, mx_, smx_ > &nu, const Matrix< T_, ma_, na_, sm_, sn_ > &tau)
Computes the positively oriented orthogonal vector to the columns of a 3x2 matrix.
Vector< T_, m_ > orthogonal(const Matrix< T_, m_, m_-1, sm_, sn_ > &tau)
Computes the positively oriented orthogonal vector to the columns of a m_ x (m_-1) Matrix.
FEAT namespace.
Definition: adjactor.hpp:12
String stringify(const T_ &item)
Converts an item into a String.
Definition: string.hpp:944
std::uint64_t Index
Index data type.
static constexpr bool is_explicit
No explicit map is available in general.
static constexpr bool is_implicit
We support implicit projection.
static constexpr int param_dim
If there was a parametrization, it would be the object's shape dim.
static constexpr int world_dim
This is a world_dim dimensional object.
Simplex shape tag struct template.
Definition: shape.hpp:44
static constexpr int dimension
Simplex dimension.
Definition: shape.hpp:48