Chi-Tech
cutmesh_3D_utils.cc
Go to the documentation of this file.
1#include "meshcutting.h"
2
4
5#include <algorithm>
6#include <queue>
7
8//###################################################################
9/**Checks the quality of a polyhedron against the following.
10 *
11 * Take each face and form triangles using the face edges and the face
12 * centroid. Now take each triangle and form a tetrahedron with the
13 * cell-centroid as the 4th vertex. The quality requirement for this tetrahedron
14 * is that its primary face must not be inverted with respect to the
15 * cell-centroid.
16 *
17 * Another optional requirement is that the cell must be convex. This is checked
18 * by using a face centroid CFC and any neighboring face's centroid NFC and normal, n_N. If
19 * the dot-product of (NFC-CFC) with n_N is negative, then the
20 * cell cannot be classified as convex.*/
23 const chi_mesh::Cell& cell,
24 const bool check_convexity/*=false*/)
25{
26 const auto& C = cell.centroid_;
27
28 for (auto& face : cell.faces_)
29 {
30 const auto& v0 = face.centroid_;
31 const size_t num_edges = face.vertex_ids_.size();
32
33 for (size_t e=0; e<num_edges; ++e)
34 {
35 auto edge = MakeEdgeFromPolygonEdgeIndex(face.vertex_ids_, e);
36
37 const auto& v1 = mesh.vertices[edge.first];
38 const auto& v2 = mesh.vertices[edge.second];
39
40 auto v01 = v1-v0;
41 auto v02 = v2-v0;
42
43 auto n = v01.Cross(v02);
44 auto C_tri = (v0+v1+v2)/3.0;
45
46 auto CC = C_tri - C;
47
48 if (CC.Dot(n)<0.0) return false;
49 }
50 }//for face
51
52 //============================================= Optional convexity check
53 if (check_convexity)
54 {
55 std::vector<std::vector<uint64_t>> proxy_faces;
56 for (const auto& face : cell.faces_)
57 proxy_faces.push_back(face.vertex_ids_);
58
59 size_t f=0;
60 for (const auto& face : cell.faces_)
61 {
62 std::set<size_t> neighbor_face_indices =
63 FindNeighborFaceIndices(proxy_faces, f);
64
65 for (size_t ofi : neighbor_face_indices)
66 {
67 auto& other_face = cell.faces_[ofi];
68 auto CFC_OFC = other_face.centroid_ - face.centroid_;
69
70 if (CFC_OFC.Dot(other_face.normal_) < 0.0)
71 return false;
72 }
73 ++f;
74 }//for f
75 }
76
77 return true;
78}
79
80
81//###################################################################
82/**Returns the face-indices that have adjacent edges to face with
83 * index face_index.*/
85 FindNeighborFaceIndices(const std::vector<std::vector<uint64_t>> &proxy_faces,
86 const size_t face_index)
87{
88 const auto& face = proxy_faces.at(face_index);
89 const size_t num_faces = proxy_faces.size();
90
91 std::set<size_t> neighbor_face_indices;
92 const size_t face_num_edges = face.size();
93 for (size_t e=0; e<face_num_edges; ++e)
94 {
95 auto face_edge = MakeEdgeFromPolygonEdgeIndex(face,e);
96 auto face_uniq_edge = MakeUniqueEdge(face_edge);
97
98 for (size_t of=0; of<num_faces; ++of)//other face
99 {
100 if (of == face_index) continue;
101 const auto& other_face = proxy_faces[of];
102
103 const size_t other_face_num_edges = other_face.size();
104 for (size_t ofe=0; ofe<other_face_num_edges; ++ofe)//other face edge
105 {
106 auto other_face_edge = MakeEdgeFromPolygonEdgeIndex(other_face, ofe);
107 auto other_face_uniq_edge = MakeUniqueEdge(other_face_edge);
108
109 if (face_uniq_edge == other_face_uniq_edge)
110 {
111 neighbor_face_indices.insert(of);
112 break; //from ofe-loop
113 }
114 }//for ofe
115 }//for of
116 }//for face edge
117
118 return neighbor_face_indices;
119}
120
121
122//###################################################################
123/**Finds the non-manifold edges of a collection of faces.*/
124std::vector<chi_mesh::mesh_cutting::Edge> chi_mesh::mesh_cutting::
125 FindNonManifoldEdges(const std::vector<std::vector<uint64_t>>& proxy_faces)
126{
127 std::vector<Edge> non_manifold_edges;
128 //The reserve amount below is chosen as follows:
129 //If we cut a tet the number of non-manifold edges will be max 3.
130 //A Hex will be 4. Arbitrary polyhedra as produced by STAR-CCM+ are
131 //on average hexagons so lets fluff this by an additional 4 edges
132 //and call 10 edges a good estimate.
133 non_manifold_edges.reserve(10);
134
135 //=================================== Build edges for each proxy face
136 // This saves us some readability later on
137 const size_t num_proxy_faces = proxy_faces.size();
138 std::vector<std::vector<Edge>> faces_edges;
139 faces_edges.reserve(num_proxy_faces);
140 for (const auto& proxy_face : proxy_faces)
141 {
142 std::vector<Edge> face_edges(proxy_face.size());
143 for (size_t e=0; e<proxy_face.size(); ++e)
144 face_edges[e] = MakeEdgeFromPolygonEdgeIndex(proxy_face, e);
145
146 faces_edges.push_back(std::move(face_edges));
147 }//for proxy face
148
149 //=================================== Search each proxy face edge for
150 // being attached to an edge of any
151 // other proxy face
152 for (size_t cf=0; cf<num_proxy_faces; ++cf)
153 {
154 for (const auto& cur_edge : faces_edges[cf])
155 {
156 bool is_manifold = false;
157 for (size_t af=0; af<num_proxy_faces; ++af)
158 {
159 if (af == cf) continue;
160 for (const auto& alt_edge : faces_edges[af])
161 {
162 if (MakeUniqueEdge(cur_edge) == MakeUniqueEdge(alt_edge))
163 {
164 is_manifold = true;
165 goto before_next_edge;
166 }
167 }//for alternate edge
168 }//for alternate face
169
170 before_next_edge:
171 if (not is_manifold) {non_manifold_edges.push_back(cur_edge);}
172 }//for current edge
173 }//for current face
174
175 return non_manifold_edges;
176}
177
178
179//###################################################################
180/**Attempts to stitch edges end-to-end.*/
181std::vector<chi_mesh::mesh_cutting::Edge> chi_mesh::mesh_cutting::
182 StitchEdgesEndToEnd(const std::vector<Edge>& edges)
183{
184 const std::string fname = __FUNCTION__;
185 std::vector<Edge> stitched_nm_edges;
186 stitched_nm_edges.reserve(edges.size());
187
188 //=================================== Declaring two queues used to
189 // stitch
190 std::queue<Edge> unused_candidate_edges;
191 std::queue<Edge> unused_noncandidate_edges;
192
193 // All the edges are initially candidates
194 for (auto& edge : edges)
195 unused_candidate_edges.push(edge);
196
197 // Begin the stitch using the first candidate
198 // edge. This edge is no longer part of the game
199 stitched_nm_edges.push_back(unused_candidate_edges.front());
200 unused_candidate_edges.pop();
201
202 //=================================== Attempt to add all candidate edges
203 // to the stitch
204 const size_t num_candidates = unused_candidate_edges.size();
205 for (size_t k=0; k<num_candidates; ++k)
206 {
207 const auto& edge_previously_added = stitched_nm_edges.back();
208
209 // The following while loop is gauranteed to terminate
210 // because we pop the first element each time.
211 while (not unused_candidate_edges.empty())
212 {
213 const auto& edge = unused_candidate_edges.front();
214
215 if (edge.first == edge_previously_added.second)
216 stitched_nm_edges.push_back(edge);
217 else
218 unused_noncandidate_edges.push(edge);
219
220 unused_candidate_edges.pop(); //removes first element
221 }
222
223 std::swap(unused_noncandidate_edges, unused_candidate_edges);
224 if (unused_candidate_edges.empty()) break;
225 }//for k
226
227 if (stitched_nm_edges.size() != edges.size())
228 throw std::logic_error(fname + ": stitching failed.");
229
230 return stitched_nm_edges;
231}
232
233//###################################################################
234/**Defines a polyhedron based only on its faces.*/
237 const std::vector<std::vector<uint64_t>> &raw_faces,
238 chi_mesh::Cell &cell)
239{
240 std::vector<uint64_t> cell_vertex_ids;
241
242 /**Lamda to add a vertex to a list.*/
243 auto AddIDToCellVertexIDs = [&cell_vertex_ids](uint64_t new_id)
244 {
245 auto find_result = std::find(cell_vertex_ids.begin(),
246 cell_vertex_ids.end(),
247 new_id);
248
249 if (find_result == cell_vertex_ids.end())
250 cell_vertex_ids.push_back(new_id);
251 };
252
253 //======================================== Build faces
254 cell.faces_.clear();
255 cell.faces_.reserve(raw_faces.size());
256 for (auto& raw_face : raw_faces)
257 {
258 chi_mesh::Vector3 face_centroid;
259 for (uint64_t vid : raw_face)
260 {
261 face_centroid += mesh.vertices[vid];
262 AddIDToCellVertexIDs(vid);
263 }
264 face_centroid /= static_cast<double>(raw_face.size());
265
266 chi_mesh::CellFace new_face;
267 new_face.centroid_ = face_centroid;
268 new_face.vertex_ids_ = raw_face;
269
270 cell.faces_.push_back(std::move(new_face));
271 }//for raw face
272
273 //======================================== Compute cell centroid
274 chi_mesh::Vector3 cell_centroid;
275 for (uint64_t vid : cell_vertex_ids)
276 cell_centroid += mesh.vertices[vid];
277 cell_centroid /= static_cast<double>(cell_vertex_ids.size());
278
279 cell.centroid_ = cell_centroid;
280 cell.vertex_ids_ = cell_vertex_ids;
281}
std::vector< uint64_t > vertex_ids_
Definition: cell.h:38
Vertex centroid_
The face centroid.
Definition: cell.h:40
std::vector< CellFace > faces_
Definition: cell.h:82
Vertex centroid_
Definition: cell.h:78
std::vector< uint64_t > vertex_ids_
Definition: cell.h:81
Edge MakeUniqueEdge(const Edge &edge)
std::set< size_t > FindNeighborFaceIndices(const std::vector< std::vector< uint64_t > > &proxy_faces, size_t face_index)
Edge MakeEdgeFromPolygonEdgeIndex(const std::vector< uint64_t > &vertex_ids, size_t edge_index)
std::vector< Edge > FindNonManifoldEdges(const std::vector< std::vector< uint64_t > > &proxy_faces)
void PopulatePolyhedronFromFaces(const MeshContinuum &mesh, const std::vector< std::vector< uint64_t > > &raw_faces, chi_mesh::Cell &cell)
std::vector< Edge > StitchEdgesEndToEnd(const std::vector< Edge > &edges)
bool CheckPolyhedronQuality(const MeshContinuum &mesh, const chi_mesh::Cell &cell, bool check_convexity=false)