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-------------------------------------------------------------------------------
11License
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
36namespace Foam
37{
39}
40
41
42// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
43
44namespace 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
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
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
162void 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
193void 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.
252void 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
326bool 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
364bool 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// ************************************************************************* //
label n
direction orient
The x/y/z orientation (0,1,2)
Definition: PDRobstacle.H:116
label edgeToFace()
Propagate from edge to face.
label faceToEdge()
Propagate from face to edge.
Describes the interaction of a face and a point. It carries the info of a successful hit and (if succ...
Definition: PointHit.H:54
const point_type & rawPoint() const noexcept
The point, no checks.
Definition: PointHit.H:172
scalar distance() const noexcept
Return distance to hit.
Definition: PointHit.H:139
const labelList & meshPoints() const
Return labelList of mesh points in patch.
const Field< point_type > & points() const noexcept
Return reference to global points.
const point & max() const
Maximum describing the bounding box.
Definition: boundBoxI.H:97
vector span() const
The bounding box span (from minimum to maximum)
Definition: boundBoxI.H:127
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:66
A subset of mesh faces organised as a primitive patch.
Definition: faceZone.H:67
Given point flip all faces such that normals point in same direction.
static bool orient(triSurface &, const point &, const bool orientOutside)
Flip faces such that normals are consistent with point:
orientedSurface()
Default construct.
Standard boundBox with extra functionality for use in octree.
Definition: treeBoundBox.H:89
Helper class to search on triSurface.
sideType
On which side of surface.
static vector surfaceNormal(const triSurface &surf, const label nearestFacei, const point &nearestPt)
Triangle (unit) normal. If nearest point to triangle on edge use.
static sideType surfaceSide(const triSurface &surf, const point &sample, const label nearestFacei)
Given nearest point (to sample) on surface determines which side.
Triangulated surface description with patch information.
Definition: triSurface.H:79
labelledTri face_type
The face type (same as the underlying PrimitivePatch)
Definition: triSurface.H:209
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
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))
label nZones
Namespace for OpenFOAM.
List< label > labelList
A List of labels.
Definition: List.H:66
static bool consistentEdge(const edge &e, const Face &f0, const Face &f1)
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
vector point
Point is a vector.
Definition: point.H:43
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
errorManip< error > abort(error &err)
Definition: errorManip.H:144
error FatalError
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:158
volScalarField & e
Definition: createFields.H:11
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333