orientedSurface.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2020 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "orientedSurface.H"
30 #include "triSurfaceTools.H"
31 #include "triSurfaceSearch.H"
32 #include "treeBoundBox.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38  defineTypeNameAndDebug(orientedSurface, 0);
39 }
40 
41 
42 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
43 
44 namespace Foam
45 {
46  // True if edge is used in opposite order in faces
47  template<class Face>
48  static inline bool consistentEdge
49  (
50  const edge& e,
51  const Face& f0,
52  const Face& f1
53  )
54  {
55  return (f0.edgeDirection(e) > 0) ^ (f1.edgeDirection(e) > 0);
56  }
57 
58 } // End namespace Foam
59 
60 
61 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
62 
63 Foam::labelList Foam::orientedSurface::faceToEdge
64 (
65  const triSurface& s,
66  const labelList& changedFaces
67 )
68 {
69  labelList changedEdges(3*changedFaces.size());
70  label changedI = 0;
71 
72  for (const label facei : changedFaces)
73  {
74  for (const label edgei : s.faceEdges()[facei])
75  {
76  changedEdges[changedI++] = edgei;
77  }
78  }
79  changedEdges.setSize(changedI);
80 
81  return changedEdges;
82 }
83 
84 
85 Foam::labelList Foam::orientedSurface::edgeToFace
86 (
87  const triSurface& s,
88  const labelList& changedEdges,
89  labelList& flip
90 )
91 {
92  labelList changedFaces(2*changedEdges.size());
93  label changedI = 0;
94 
95  for (const label edgeI : changedEdges)
96  {
97  const labelList& eFaces = s.edgeFaces()[edgeI];
98 
99  if (eFaces.size() < 2)
100  {
101  // Do nothing, faces was already visited.
102  }
103  else if (eFaces.size() == 2)
104  {
105  const label face0 = eFaces[0];
106  const label face1 = eFaces[1];
107 
108  const triSurface::face_type& f0 = s.localFaces()[face0];
109  const triSurface::face_type& f1 = s.localFaces()[face1];
110 
111  if (flip[face0] == UNVISITED)
112  {
113  if (flip[face1] == UNVISITED)
114  {
116  << abort(FatalError);
117  }
118  else
119  {
120  // Face1 has a flip state, face0 hasn't
121  if (consistentEdge(s.edges()[edgeI], f0, f1))
122  {
123  // Take over flip status
124  flip[face0] = (flip[face1] == FLIP ? FLIP : NOFLIP);
125  }
126  else
127  {
128  // Invert
129  flip[face0] = (flip[face1] == FLIP ? NOFLIP : FLIP);
130  }
131  changedFaces[changedI++] = face0;
132  }
133  }
134  else
135  {
136  if (flip[face1] == UNVISITED)
137  {
138  // Face0 has a flip state, face1 hasn't
139  if (consistentEdge(s.edges()[edgeI], f0, f1))
140  {
141  flip[face1] = (flip[face0] == FLIP ? FLIP : NOFLIP);
142  }
143  else
144  {
145  flip[face1] = (flip[face0] == FLIP ? NOFLIP : FLIP);
146  }
147  changedFaces[changedI++] = face1;
148  }
149  }
150  }
151  else
152  {
153  // Multiply connected. Do what?
154  }
155  }
156  changedFaces.setSize(changedI);
157 
158  return changedFaces;
159 }
160 
161 
162 void Foam::orientedSurface::walkSurface
163 (
164  const triSurface& s,
165  const label startFacei,
166  labelList& flipState
167 )
168 {
169  // List of faces that were changed in the last iteration.
170  labelList changedFaces(1, startFacei);
171  // List of edges that were changed in the last iteration.
172  labelList changedEdges;
173 
174  while (true)
175  {
176  changedEdges = faceToEdge(s, changedFaces);
177 
178  if (changedEdges.empty())
179  {
180  break;
181  }
182 
183  changedFaces = edgeToFace(s, changedEdges, flipState);
184 
185  if (changedFaces.empty())
186  {
187  break;
188  }
189  }
190 }
191 
192 
193 void Foam::orientedSurface::propagateOrientation
194 (
195  const triSurface& s,
196  const point& samplePoint,
197  const bool orientOutside,
198  const label nearestFacei,
199  const point& nearestPt,
200  labelList& flipState
201 )
202 {
203  //
204  // Determine orientation to normal on nearest face
205  //
207  (
208  s,
209  samplePoint,
210  nearestFacei
211  );
212 
213  if (side == triSurfaceTools::UNKNOWN)
214  {
215  // Non-closed surface. Do what? For now behave as if no flipping
216  // necessary
217  flipState[nearestFacei] = NOFLIP;
218  }
219  else if ((side == triSurfaceTools::OUTSIDE) == orientOutside)
220  {
221  // outside & orientOutside or inside & !orientOutside
222  // Normals on surface pointing correctly. No need to flip normals
223  flipState[nearestFacei] = NOFLIP;
224  }
225  else
226  {
227  // Need to flip normals.
228  flipState[nearestFacei] = FLIP;
229  }
230 
231  if (debug)
232  {
233  vector n = triSurfaceTools::surfaceNormal(s, nearestFacei, nearestPt);
234 
235  Pout<< "orientedSurface::propagateOrientation : starting face"
236  << " orientation:" << nl
237  << " for samplePoint:" << samplePoint << nl
238  << " starting from point:" << nearestPt << nl
239  << " on face:" << nearestFacei << nl
240  << " with normal:" << n << nl
241  << " decided side:" << label(side)
242  << endl;
243  }
244 
245  // Walk the surface from nearestFacei, changing the flipstate.
246  walkSurface(s, nearestFacei, flipState);
247 }
248 
249 
250 // Find side for zoneI only by counting the number of intersections. Determines
251 // if face is oriented consistent with outwards pointing normals.
252 void Foam::orientedSurface::findZoneSide
253 (
254  const triSurfaceSearch& surfSearches,
255  const labelList& faceZone,
256  const label zoneI,
257  const point& outsidePoint,
258  label& zoneFacei,
259  bool& isOutside
260 )
261 {
262  const triSurface& s = surfSearches.surface();
263 
264  zoneFacei = -1;
265  isOutside = false;
266 
267  pointField start(1, outsidePoint);
268  List<List<pointIndexHit>> hits(1, List<pointIndexHit>());
269 
270  forAll(faceZone, facei)
271  {
272  if (faceZone[facei] == zoneI)
273  {
274  const point& fc = s.faceCentres()[facei];
275  const vector& n = s.faceNormals()[facei];
276 
277  const vector d = fc - outsidePoint;
278  const scalar magD = mag(d);
279 
280  // Check if normal different enough to decide upon
281  if (magD > SMALL && (mag(n & d/magD) > 1e-6))
282  {
283  pointField end(1, fc + d);
284 
285  //Info<< "Zone " << zoneI << " : Shooting ray"
286  // << " from " << outsidePoint
287  // << " to " << end
288  // << " to pierce triangle " << facei
289  // << " with centre " << fc << endl;
290 
291 
292  surfSearches.findLineAll(start, end, hits);
293 
294  label zoneIndex = -1;
295  forAll(hits[0], i)
296  {
297  if (hits[0][i].index() == facei)
298  {
299  zoneIndex = i;
300  break;
301  }
302  }
303 
304  if (zoneIndex != -1)
305  {
306  zoneFacei = facei;
307 
308  if ((zoneIndex%2) == 0)
309  {
310  // Odd number of intersections. Check if normal points
311  // in direction of ray
312  isOutside = ((n & d) < 0);
313  }
314  else
315  {
316  isOutside = ((n & d) > 0);
317  }
318  break;
319  }
320  }
321  }
322  }
323 }
324 
325 
326 bool Foam::orientedSurface::flipSurface
327 (
328  triSurface& s,
329  const labelList& flipState
330 )
331 {
332  bool hasFlipped = false;
333 
334  // Flip tris in s
335  forAll(flipState, facei)
336  {
337  if (flipState[facei] == UNVISITED)
338  {
340  << "unvisited face " << facei
341  << abort(FatalError);
342  }
343  else if (flipState[facei] == FLIP)
344  {
345  labelledTri& tri = s[facei];
346 
347  label tmp = tri[0];
348 
349  tri[0] = tri[1];
350  tri[1] = tmp;
351 
352  hasFlipped = true;
353  }
354  }
355  // Recalculate normals
356  if (hasFlipped)
357  {
358  s.clearOut();
359  }
360  return hasFlipped;
361 }
362 
363 
364 bool Foam::orientedSurface::orientConsistent(triSurface& s)
365 {
366  bool anyFlipped = false;
367 
368  // Do initial flipping to make triangles consistent. Otherwise if the
369  // nearest is e.g. on an edge inbetween inconsistent triangles it might
370  // make the wrong decision.
371  if (s.size() > 0)
372  {
373  // Whether face has to be flipped.
374  // UNVISITED: unvisited
375  // NOFLIP: no need to flip
376  // FLIP: need to flip
377  labelList flipState(s.size(), UNVISITED);
378 
379  label facei = 0;
380  while (true)
381  {
382  label startFacei = -1;
383  while (facei < s.size())
384  {
385  if (flipState[facei] == UNVISITED)
386  {
387  startFacei = facei;
388  break;
389  }
390  facei++;
391  }
392 
393  if (startFacei == -1)
394  {
395  break;
396  }
397 
398  flipState[startFacei] = NOFLIP;
399  walkSurface(s, startFacei, flipState);
400  }
401 
402  anyFlipped = flipSurface(s, flipState);
403  }
404  return anyFlipped;
405 }
406 
407 
408 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
409 
410 // Null constructor
412 :
413  triSurface()
414 {}
415 
416 
417 // Construct from surface and point which defines outside
419 (
420  const triSurface& surf,
421  const point& samplePoint,
422  const bool orientOutside
423 )
424 :
425  triSurface(surf)
426 {
427  orient(*this, samplePoint, orientOutside);
428 }
429 
430 
431 // Construct from surface. Calculate outside point.
433 (
434  const triSurface& surf,
435  const bool orientOutside
436 )
437 :
438  triSurface(surf)
439 {
440  // BoundBox calculation without localPoints
441  treeBoundBox bb(surf.points(), surf.meshPoints());
442 
443  point outsidePoint = bb.max() + bb.span();
444 
445  orient(*this, outsidePoint, orientOutside);
446 }
447 
448 
449 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
450 
452 (
453  triSurface& s,
454  const point& samplePoint,
455  const bool orientOutside
456 )
457 {
458  // Do initial flipping to make triangles consistent. Otherwise if the
459  // nearest is e.g. on an edge inbetween inconsistent triangles it might
460  // make the wrong decision.
461  bool topoFlipped = orientConsistent(s);
462 
463 
464  // Whether face has to be flipped.
465  // UNVISITED: unvisited
466  // NOFLIP: no need to flip
467  // FLIP: need to flip
468  labelList flipState(s.size(), UNVISITED);
469 
470 
471  while (true)
472  {
473  // Linear search for nearest unvisited point on surface.
474 
475  scalar minDist = GREAT;
476  point minPoint;
477  label minFacei = -1;
478 
479  forAll(s, facei)
480  {
481  if (flipState[facei] == UNVISITED)
482  {
483  pointHit curHit =
484  s[facei].nearestPoint(samplePoint, s.points());
485 
486  if (curHit.distance() < minDist)
487  {
488  minDist = curHit.distance();
489  minPoint = curHit.rawPoint();
490  minFacei = facei;
491  }
492  }
493  }
494 
495  // Did we find anything?
496  if (minFacei == -1)
497  {
498  break;
499  }
500 
501  // From this nearest face see if needs to be flipped and then
502  // go outwards.
503  propagateOrientation
504  (
505  s,
506  samplePoint,
507  orientOutside,
508  minFacei,
509  minPoint,
510  flipState
511  );
512  }
513 
514  // Now finally flip triangles according to flipState.
515  bool geomFlipped = flipSurface(s, flipState);
516 
517  return topoFlipped || geomFlipped;
518 }
519 
520 
522 (
523  triSurface& s,
524  const triSurfaceSearch& querySurf,
525  const point& samplePoint,
526  const bool orientOutside
527 )
528 {
529  // Do initial flipping to make triangles consistent. Otherwise if the
530  // nearest is e.g. on an edge inbetween inconsistent triangles it might
531  // make the wrong decision.
532  bool topoFlipped = orientConsistent(s);
533 
534  // Determine disconnected parts of surface
535  boolList borderEdge(s.nEdges(), false);
536  forAll(s.edgeFaces(), edgeI)
537  {
538  if (s.edgeFaces()[edgeI].size() != 2)
539  {
540  borderEdge[edgeI] = true;
541  }
542  }
544  label nZones = s.markZones(borderEdge, faceZone);
545 
546  // Check intersection with one face per zone.
547 
548  labelList flipState(s.size(), UNVISITED);
549  for (label zoneI = 0; zoneI < nZones; zoneI++)
550  {
551  label zoneFacei = -1;
552  bool isOutside;
553  findZoneSide
554  (
555  querySurf,
556  faceZone,
557  zoneI,
558  samplePoint,
559 
560  zoneFacei,
561  isOutside
562  );
563 
564  if (isOutside == orientOutside)
565  {
566  flipState[zoneFacei] = NOFLIP;
567  }
568  else
569  {
570  flipState[zoneFacei] = FLIP;
571  }
572  walkSurface(s, zoneFacei, flipState);
573  }
574 
575  // Now finally flip triangles according to flipState.
576  bool geomFlipped = flipSurface(s, flipState);
577 
578  return topoFlipped || geomFlipped;
579 }
580 
581 
582 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::PrimitivePatch::points
const Field< point_type > & points() const noexcept
Return reference to global points.
Definition: PrimitivePatch.H:299
nZones
label nZones
Definition: interpolatedFaces.H:24
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:67
Foam::pointField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
s
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Definition: gmvOutputSpray.H:25
Foam::PointHit
Describes the interaction of a face and a point. It carries the info of a successful hit and (if succ...
Definition: PointHit.H:53
Foam::triSurfaceTools::surfaceNormal
static vector surfaceNormal(const triSurface &surf, const label nearestFacei, const point &nearestPt)
Triangle (unit) normal. If nearest point to triangle on edge use.
Definition: triSurfaceTools.C:1955
Foam::orientedSurface::orientedSurface
orientedSurface()
Default construct.
Definition: orientedSurface.C:411
Foam::treeBoundBox
Standard boundBox with extra functionality for use in octree.
Definition: treeBoundBox.H:86
Foam::edge
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:63
Foam::PointHit::distance
scalar distance() const noexcept
Return distance to hit.
Definition: PointHit.H:139
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::Pout
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Foam::triSurfaceSearch
Helper class to search on triSurface.
Definition: triSurfaceSearch.H:58
Foam::consistentEdge
static bool consistentEdge(const edge &e, const Face &f0, const Face &f1)
Definition: orientedSurface.C:49
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
n
label n
Definition: TABSMDCalcMethod2.H:31
Foam::triSurface
Triangulated surface description with patch information.
Definition: triSurface.H:76
Foam::faceZone
A subset of mesh faces organised as a primitive patch.
Definition: faceZone.H:64
treeBoundBox.H
Foam::List::setSize
void setSize(const label n)
Alias for resize()
Definition: List.H:222
Foam::PointHit::rawPoint
const point_type & rawPoint() const noexcept
The point, no checks.
Definition: PointHit.H:172
Foam::FatalError
error FatalError
stdFoam::end
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:121
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::abort
errorManip< error > abort(error &err)
Definition: errorManip.H:144
Foam::triSurfaceTools::UNKNOWN
Definition: triSurfaceTools.H:452
Foam::vector
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:51
Foam::triSurfaceTools::surfaceSide
static sideType surfaceSide(const triSurface &surf, const point &sample, const label nearestFacei)
Given nearest point (to sample) on surface determines which side.
Definition: triSurfaceTools.C:2036
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::VectorSpace::max
static const Form max
Definition: VectorSpace.H:117
Foam::Vector< scalar >
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:63
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::triSurfaceTools::OUTSIDE
Definition: triSurfaceTools.H:454
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
triSurfaceSearch.H
Foam::triSurface::face_type
labelledTri face_type
The face type (same as the underlying PrimitivePatch)
Definition: triSurface.H:209
Foam::orientedSurface::orient
static bool orient(triSurface &, const point &, const bool orientOutside)
Flip faces such that normals are consistent with point:
Definition: orientedSurface.C:452
Foam::point
vector point
Point is a vector.
Definition: point.H:43
Foam::PrimitivePatch::meshPoints
const labelList & meshPoints() const
Return labelList of mesh points in patch.
Definition: PrimitivePatch.C:330
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
Foam::triSurfaceTools::sideType
sideType
On which side of surface.
Definition: triSurfaceTools.H:450
triSurfaceTools.H
orientedSurface.H