surfaceHookUp.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2014-2026 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 Application
25  surfaceHookUp
26 
27 Description
28  Find close open edges and stitches the surface along them
29 
30 Usage
31  - surfaceHookUp \‍( surface1.stl surface2.stl \‍) hookDistance
32 
33 \*---------------------------------------------------------------------------*/
34 
35 #include "argList.H"
36 #include "triSurface.H"
37 #include "indexedOctree.H"
38 #include "treeDataEdge.H"
39 #include "PackedBoolList.H"
40 
41 using namespace Foam;
42 
43 // Split facei along edgeI at position newPointi
44 void greenRefine
45 (
46  const triSurface& surf,
47  const label facei,
48  const label edgeI,
49  const label newPointi,
50  DynamicList<labelledTri>& newFaces
51 )
52 {
53  const labelledTri& f = surf.localFaces()[facei];
54  const edge& e = surf.edges()[edgeI];
55 
56  // Find index of edge in face.
57 
58  label fp0 = findIndex(f, e[0]);
59  label fp1 = f.fcIndex(fp0);
60  label fp2 = f.fcIndex(fp1);
61 
62  if (f[fp1] == e[1])
63  {
64  // Edge oriented like face
65  newFaces.append
66  (
68  (
69  f[fp0],
70  newPointi,
71  f[fp2],
72  f.region()
73  )
74  );
75  newFaces.append
76  (
78  (
79  newPointi,
80  f[fp1],
81  f[fp2],
82  f.region()
83  )
84  );
85  }
86  else
87  {
88  newFaces.append
89  (
91  (
92  f[fp2],
93  newPointi,
94  f[fp1],
95  f.region()
96  )
97  );
98  newFaces.append
99  (
101  (
102  newPointi,
103  f[fp0],
104  f[fp1],
105  f.region()
106  )
107  );
108  }
109 }
110 
111 
112 void createBoundaryEdgeTrees
113 (
114  const PtrList<triSurface>& surfs,
116  labelListList& treeBoundaryEdges
117 )
118 {
119  forAll(surfs, surfI)
120  {
121  const triSurface& surf = surfs[surfI];
122 
123  // Boundary edges
124  treeBoundaryEdges[surfI] =
125  labelList
126  (
128  (
129  surf.nInternalEdges(),
130  surf.nEdges() - surf.nInternalEdges()
131  )
132  );
133 
134  randomGenerator rndGen(17301893);
135 
136  // Slightly extended bb. Slightly off-centred just so on symmetric
137  // geometry there are less face/edge aligned items.
138  treeBoundBox bb
139  (
141  );
142 
143  bEdgeTrees.set
144  (
145  surfI,
147  (
149  (
150  false, // cachebb
151  surf.edges(), // edges
152  surf.localPoints(), // points
153  treeBoundaryEdges[surfI] // selected edges
154  ),
155  bb, // bb
156  8, // maxLevel
157  10, // leafsize
158  3.0 // duplicity
159  )
160  );
161  }
162 }
163 
164 
165 class findNearestOpSubset
166 {
167  const indexedOctree<treeDataEdge>& tree_;
168 
169  DynamicList<label>& shapeMask_;
170 
171 public:
172 
173  findNearestOpSubset
174  (
175  const indexedOctree<treeDataEdge>& tree,
176  DynamicList<label>& shapeMask
177  )
178  :
179  tree_(tree),
180  shapeMask_(shapeMask)
181  {}
182 
183  void operator()
184  (
185  const labelUList& indices,
186  const point& sample,
187 
188  scalar& nearestDistSqr,
189  label& minIndex,
190  point& nearestPoint
191  ) const
192  {
193  const treeDataEdge& shape = tree_.shapes();
194 
195  forAll(indices, i)
196  {
197  const label index = indices[i];
198  const label edgeIndex = shape.edgeLabels()[index];
199 
200  if
201  (
202  !shapeMask_.empty()
203  && findIndex(shapeMask_, edgeIndex) != -1
204  )
205  {
206  continue;
207  }
208 
209  const edge& e = shape.edges()[edgeIndex];
210 
211  pointHit nearHit = e.line(shape.points()).nearestDist(sample);
212 
213  // Only register hit if closest point is not an edge point
214  if (nearHit.hit())
215  {
216  scalar distSqr = sqr(nearHit.distance());
217 
218  if (distSqr < nearestDistSqr)
219  {
220  nearestDistSqr = distSqr;
221  minIndex = index;
222  nearestPoint = nearHit.rawPoint();
223  }
224  }
225  }
226  }
227 };
228 
229 
230 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
231 
232 int main(int argc, char *argv[])
233 {
235  (
236  "Hook surfaces to other surfaces by moving and re-triangulating \n"
237  "their boundary edges to match other surface boundary edges"
238  );
239 
240  #include "removeCaseOptions.H"
241 
243  argList::validArgs.append("surface files");
244  argList::validArgs.append("hook distance");
245  argList args(argc, argv);
246 
247  const fileNameList surfFileNames(args.argRead<fileNameList>(1));
248 
249  Info<< endl;
250  PtrList<triSurface> surfs(surfFileNames.size());
251  forAll(surfs, surfi)
252  {
253  surfs.set(surfi, new triSurface(surfFileNames[surfi]));
254 
255  Info<< "Reading "
256  << surfFileNames[surfi].c_str() << ':' << endl;
257 
258  Info<< incrIndent << indent << "Regions = (";
259  forAll(surfs[surfi].patches(), surfPatchi)
260  {
261  if (surfPatchi) Info << ' ';
262  Info<< surfs[surfi].patches()[surfPatchi].name();
263  }
264  Info << ')' << decrIndent << nl << endl;
265  }
266 
267  const scalar dist(args.argRead<scalar>(2));
268  const scalar matchTolerance(max(1e-6*dist, small));
269  Info<< "Hooking distance = " << dist << endl;
270 
271  const label maxIters = 100;
272 
273  PtrList<indexedOctree<treeDataEdge>> bEdgeTrees(surfs.size());
274  labelListList treeBoundaryEdges(surfs.size());
275 
276  List<DynamicList<labelledTri>> newFaces(surfs.size());
277  List<DynamicList<point>> newPoints(surfs.size());
278  List<PackedBoolList> visitedFace(surfs.size());
279 
280  PtrList<triSurface> newSurfs(surfs.size());
281  forAll(surfs, surfi)
282  {
283  newSurfs.set(surfi, new triSurface(surfs[surfi]));
284  }
285 
286  label nChanged = 0;
287  label nIters = 1;
288 
289  do
290  {
291  Info<< nl << "Iteration = " << nIters++ << endl;
292  nChanged = 0;
293 
294  createBoundaryEdgeTrees(newSurfs, bEdgeTrees, treeBoundaryEdges);
295 
296  forAll(newSurfs, surfI)
297  {
298  const triSurface& newSurf = newSurfs[surfI];
299 
300  newFaces[surfI] = newSurf.localFaces();
301  newPoints[surfI] = newSurf.localPoints();
302  visitedFace[surfI] = PackedBoolList(newSurf.size(), false);
303  }
304 
305  forAll(newSurfs, surfI)
306  {
307  const triSurface& surf = newSurfs[surfI];
308 
309  List<pointIndexHit> bPointsTobEdges(surf.boundaryPoints().size());
310  labelList bPointsHitTree(surf.boundaryPoints().size(), -1);
311 
312  const labelListList& pointEdges = surf.pointEdges();
313 
314  forAll(bPointsTobEdges, bPointi)
315  {
316  pointIndexHit& nearestHit = bPointsTobEdges[bPointi];
317 
318  const label pointi = surf.boundaryPoints()[bPointi];
319  const point& samplePt = surf.localPoints()[pointi];
320 
321  const labelList& pEdges = pointEdges[pointi];
322 
323  // Add edges connected to the edge to the shapeMask
324  DynamicList<label> shapeMask;
325  shapeMask.append(pEdges);
326 
327  forAll(bEdgeTrees, treeI)
328  {
329  const indexedOctree<treeDataEdge>& bEdgeTree =
330  bEdgeTrees[treeI];
331 
332  pointIndexHit currentHit =
333  bEdgeTree.findNearest
334  (
335  samplePt,
336  sqr(dist),
337  findNearestOpSubset
338  (
339  bEdgeTree,
340  shapeMask
341  )
342  );
343 
344  if
345  (
346  currentHit.hit()
347  &&
348  (
349  !nearestHit.hit()
350  ||
351  (
352  magSqr(currentHit.hitPoint() - samplePt)
353  < magSqr(nearestHit.hitPoint() - samplePt)
354  )
355  )
356  )
357  {
358  nearestHit = currentHit;
359  bPointsHitTree[bPointi] = treeI;
360  }
361  }
362 
363  scalar dist2 = magSqr(nearestHit.rawPoint() - samplePt);
364 
365  if (nearestHit.hit())
366  {
367  if (dist2 > Foam::sqr(dist))
368  {
369  nearestHit.setMiss();
370  }
371  }
372  }
373 
374  forAll(bPointsTobEdges, bPointi)
375  {
376  const pointIndexHit& eHit = bPointsTobEdges[bPointi];
377 
378  if (eHit.hit())
379  {
380  const label hitSurfI = bPointsHitTree[bPointi];
381  const triSurface& hitSurf = newSurfs[hitSurfI];
382 
383  const label eIndex =
384  treeBoundaryEdges[hitSurfI][eHit.index()];
385  const edge& e = hitSurf.edges()[eIndex];
386 
387  const label pointi = surf.boundaryPoints()[bPointi];
388 
389  const labelList& eFaces = hitSurf.edgeFaces()[eIndex];
390 
391  if (eFaces.size() != 1)
392  {
394  << "Edge is attached to " << eFaces.size()
395  << " faces." << endl;
396 
397  continue;
398  }
399 
400  const label facei = eFaces[0];
401 
402  if (visitedFace[hitSurfI][facei])
403  {
404  continue;
405  }
406 
407  DynamicList<labelledTri> newFacesFromSplit(2);
408 
409  const point& pt = surf.localPoints()[pointi];
410 
411  if
412  (
413  (
414  magSqr(pt - hitSurf.localPoints()[e.start()])
415  < matchTolerance
416  )
417  || (
418  magSqr(pt - hitSurf.localPoints()[e.end()])
419  < matchTolerance
420  )
421  )
422  {
423  continue;
424  }
425 
426  nChanged++;
427 
428  label newPointi = -1;
429 
430  // Keep the points in the same place and move the edge
431  if (hitSurfI == surfI)
432  {
433  newPointi = pointi;
434  }
435  else
436  {
437  newPoints[hitSurfI].append(newPoints[surfI][pointi]);
438  newPointi = newPoints[hitSurfI].size() - 1;
439  }
440 
441  // Split the other face.
442  greenRefine
443  (
444  hitSurf,
445  facei,
446  eIndex,
447  newPointi,
448  newFacesFromSplit
449  );
450 
451  visitedFace[hitSurfI][facei] = true;
452 
453  forAll(newFacesFromSplit, newFacei)
454  {
455  const labelledTri& fN = newFacesFromSplit[newFacei];
456 
457  if (newFacei == 0)
458  {
459  newFaces[hitSurfI][facei] = fN;
460  }
461  else
462  {
463  newFaces[hitSurfI].append(fN);
464  }
465  }
466  }
467  }
468  }
469 
470  Info<< " Number of edges split = " << nChanged << endl;
471 
472  forAll(newSurfs, surfi)
473  {
474  newSurfs.set
475  (
476  surfi,
477  new triSurface
478  (
479  newFaces[surfi],
480  newSurfs[surfi].patches(),
481  pointField(newPoints[surfi])
482  )
483  );
484  }
485 
486  } while (nChanged > 0 && nIters <= maxIters);
487 
488  Info<< endl;
489 
490  forAll(newSurfs, surfi)
491  {
492  const fileName newSurfFileName =
493  surfFileNames[surfi].lessExt()
494  + "_hooked."
495  + surfFileNames[surfi].ext();
496 
497  Info<< "Writing hooked surface " << newSurfFileName << endl;
498 
499  newSurfs[surfi].write(newSurfFileName);
500  }
501 
502  Info<< "\nEnd\n" << endl;
503 
504  return 0;
505 }
506 
507 
508 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:78
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
virtual Ostream & write(const token &)
Write token.
Definition: Ostream.C:51
A bit-packed bool list.
scalar distance() const
Return distance to hit.
Definition: PointHit.H:139
const Point & rawPoint() const
Return point with no checking.
Definition: PointHit.H:158
bool hit() const
Is there a hit.
Definition: PointHit.H:120
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:54
const Point & hitPoint() const
Return hit point.
const Point & rawPoint() const
Return point with no checking.
label index() const
Return index.
bool hit() const
Is there a hit.
label nEdges() const
Return number of edges in patch.
const labelListList & pointEdges() const
Return point-edge addressing.
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
label nInternalEdges() const
Number of internal edges.
const List< FaceType > & localFaces() const
Return patch faces addressing into local point list.
const labelList & boundaryPoints() const
Return list of boundary points,.
const labelListList & edgeFaces() const
Return edge-face addressing.
const Field< PointType > & localPoints() const
Return pointField of points in patch.
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: PtrList.H:75
bool set(const label) const
Is element set.
Definition: PtrListI.H:62
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: UList.H:74
label fcIndex(const label i) const
Return the forward circular index, i.e. the next index.
Definition: UListI.H:58
bool empty() const
Return true if the UList is empty (ie, size() is zero)
Definition: UListI.H:325
label size() const
Return the number of elements in the UPtrList.
Definition: UPtrListI.H:29
Extract command arguments and options from the supplied argc and argv parameters.
Definition: argList.H:103
static void addNote(const string &)
Add extra notes for the usage information.
Definition: argList.C:152
static void noParallel()
Remove the parallel options.
Definition: argList.C:168
static SLList< string > validArgs
A list of valid (mandatory) arguments.
Definition: argList.H:153
T argRead(const label index) const
Read a value from the argument at index.
Definition: argListI.H:234
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:61
A class for handling file names.
Definition: fileName.H:82
fileName lessExt() const
Return file name without extension (part before last .)
Definition: fileName.C:303
word ext() const
Return file name extension (part after last .)
Definition: fileName.C:318
Non-pointer based hierarchical recursive searching.
Definition: indexedOctree.H:72
const Type & shapes() const
Reference to shape.
Triangle with additional region number.
Definition: labelledTri.H:60
Random number generator.
Standard boundBox + extra functionality for use in octree.
Definition: treeBoundBox.H:90
treeBoundBox extend(const scalar s) const
Return asymmetrically extended bounding box, with guaranteed.
Holds data for octree to work on an edges subset.
Definition: treeDataEdge.H:54
const labelList & edgeLabels() const
Definition: treeDataEdge.H:179
const edgeList & edges() const
Definition: treeDataEdge.H:169
const pointField & points() const
Definition: treeDataEdge.H:174
label size() const
Return size.
Triangulated surface description with patch information.
Definition: triSurface.H:68
int main(int argc, char *argv[])
Definition: financialFoam.C:44
const fvPatchList & patches
#define WarningInFunction
Report a warning using Foam::Warning.
Namespace for OpenFOAM.
const doubleScalar e
Definition: doubleScalar.H:106
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:272
List< label > labelList
A List of labels.
Definition: labelList.H:56
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
messageStream Info
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:265
tmp< DimensionedField< typename outerProduct< Type, Type >::type, GeoMesh, Field >> sqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
labelList identityMap(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
tmp< DimensionedField< scalar, GeoMesh, Field > > magSqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:243
static const char nl
Definition: Ostream.H:297
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
label newPointi
Definition: readKivaGrid.H:495
labelList f(nPoints)
randomGenerator rndGen(653213)
Foam::argList args(argc, argv)