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