surfaceTransformPoints.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) 2017-2022,2024 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 Application
28  surfaceTransformPoints
29 
30 Group
31  grpSurfaceUtilities
32 
33 Description
34  Transform (scale/rotate) a surface.
35  Like transformPoints but for surfaces.
36 
37  The rollPitchYaw and yawPitchRoll options take three angles (degrees)
38  that describe the intrinsic Euler rotation.
39 
40  rollPitchYaw
41  - roll (rotation about X) followed by
42  - pitch (rotation about Y) followed by
43  - yaw (rotation about Z)
44 
45  yawPitchRoll
46  - yaw (rotation about Z) followed by
47  - pitch (rotation about Y) followed by
48  - roll (rotation about X)
49 
50 \*---------------------------------------------------------------------------*/
51 
52 #include "argList.H"
53 #include "Fstream.H"
54 #include "boundBox.H"
55 #include "transformField.H"
56 #include "Pair.H"
57 #include "Tuple2.H"
58 #include "axisAngleRotation.H"
60 #include "MeshedSurfaces.H"
61 #include "cylindricalCS.H"
62 
63 using namespace Foam;
64 using namespace Foam::coordinateRotations;
65 
66 static word getExtension(const fileName& name)
67 {
68  return
69  (
70  name.has_ext("gz")
71  ? name.stem().ext()
72  : name.ext()
73  );
74 }
75 
76 
77 // Non-short-circuiting check to get all warnings
78 static bool hasReadWriteTypes(const word& readType, const word& writeType)
79 {
80  volatile bool good = true;
81 
82  if (!meshedSurface::canReadType(readType, true))
83  {
84  good = false;
85  }
86 
87  if (!meshedSurface::canWriteType(writeType, true))
88  {
89  good = false;
90  }
91 
92  return good;
93 }
94 
95 
96 // Retrieve scaling option
97 // - size 0 : no scaling
98 // - size 1 : uniform scaling
99 // - size 3 : non-uniform scaling
100 List<scalar> getScalingOpt(const word& optName, const argList& args)
101 {
102  // readListIfPresent handles single or multiple values
103  // - accept 1 or 3 values
104 
105  List<scalar> scaling;
106  args.readListIfPresent(optName, scaling);
107 
108  if (scaling.size() == 1)
109  {
110  // Uniform scaling
111  }
112  else if (scaling.size() == 3)
113  {
114  // Non-uniform, but may actually be uniform
115  if
116  (
117  equal(scaling[0], scaling[1])
118  && equal(scaling[0], scaling[2])
119  )
120  {
121  scaling.resize(1);
122  }
123  }
124  else if (!scaling.empty())
125  {
126  FatalError
127  << "Incorrect number of components, must be 1 or 3." << nl
128  << " -" << optName << ' ' << args[optName].c_str() << endl
129  << exit(FatalError);
130  }
131 
132  if (scaling.size() == 1 && equal(scaling[0], 1))
133  {
134  // Scale factor 1 == no scaling
135  scaling.clear();
136  }
137 
138  // Zero and negative scaling are permitted
139 
140  return scaling;
141 }
142 
143 
144 void applyScaling(pointField& points, const List<scalar>& scaling)
145 {
146  if (scaling.size() == 1)
147  {
148  Info<< "Scaling points uniformly by " << scaling[0] << nl;
149  points *= scaling[0];
150  }
151  else if (scaling.size() == 3)
152  {
153  const vector factor(scaling[0], scaling[1], scaling[2]);
154  Info<< "Scaling points by " << factor << nl;
155  cmptMultiply(points, points, factor);
156  }
157 }
158 
159 
160 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
161 
162 int main(int argc, char *argv[])
163 {
165  (
166  "Transform (translate / rotate / scale) surface points.\n"
167  "Like transformPoints but for surfaces.\n"
168  "Note: roll=rotate about x, pitch=rotate about y, yaw=rotate about z"
169  );
171  argList::addArgument("input", "The input surface file");
172  argList::addArgument("output", "The output surface file");
174  (
175  "recentre",
176  "Recentre the bounding box before other operations"
177  );
179  (
180  "translate",
181  "vector",
182  "Translate by specified <vector> before rotations"
183  );
185  (
186  "auto-centre",
187  "Use bounding box centre as centre for rotations"
188  );
190  (
191  "centre",
192  "point",
193  "Use specified <point> as centre for rotations"
194  );
195  argList::addOptionCompat("auto-centre", {"auto-origin", 2206});
196  argList::addOptionCompat("centre", {"origin", 2206});
197 
199  (
200  "rotate",
201  "(vectorA vectorB)",
202  "Rotate from <vectorA> to <vectorB> - eg, '((1 0 0) (0 0 1))'"
203  );
205  (
206  "rotate-angle",
207  "(vector angle)",
208  "Rotate <angle> degrees about <vector> - eg, '((1 0 0) 45)'"
209  );
211  (
212  "rotate-x", "deg",
213  "Rotate (degrees) about x-axis"
214  );
216  (
217  "rotate-y", "deg",
218  "Rotate (degrees) about y-axis"
219  );
221  (
222  "rotate-z", "deg",
223  "Rotate (degrees) about z-axis"
224  );
226  (
227  "rollPitchYaw",
228  "vector",
229  "Rotate by '(roll pitch yaw)' degrees"
230  );
232  (
233  "yawPitchRoll",
234  "vector",
235  "Rotate by '(yaw pitch roll)' degrees"
236  );
238  (
239  "read-scale",
240  "scalar | vector",
241  "Uniform or non-uniform input scaling"
242  );
244  (
245  "write-scale",
246  "scalar | vector",
247  "Uniform or non-uniform output scaling"
248  );
250  (
251  "read-format",
252  "type",
253  "Input format (default: use file extension)"
254  );
256  (
257  "write-format",
258  "type",
259  "Output format (default: use file extension)"
260  );
262  (
263  "cylToCart",
264  "(originVec axisVec directionVec)",
265  "Tranform cylindrical coordinates to cartesian coordinates"
266  );
267 
268  // Backward compatibility and with transformPoints
269  argList::addOptionCompat("write-scale", {"scale", -2006});
270 
271  argList args(argc, argv);
272 
273  // Verify that an operation has been specified
274  {
275  const List<word> operationNames
276  ({
277  "recentre",
278  "translate",
279  "rotate",
280  "rotate-angle",
281  "rotate-x",
282  "rotate-y",
283  "rotate-z",
284  "rollPitchYaw",
285  "yawPitchRoll",
286  "read-scale",
287  "write-scale",
288  "cylToCart"
289  });
290 
291  if (!args.count(operationNames))
292  {
293  FatalError
294  << "No operation supplied, "
295  << "use at least one of the following:" << nl
296  << " ";
297 
298  for (const auto& opName : operationNames)
299  {
300  FatalError
301  << " -" << opName;
302  }
303 
304  FatalError
305  << nl << exit(FatalError);
306  }
307  }
308 
309  const auto importName = args.get<fileName>(1);
310  const auto exportName = args.get<fileName>(2);
311 
312  const word readFileType
313  (
314  args.getOrDefault<word>("read-format", getExtension(importName))
315  );
316 
317  const word writeFileType
318  (
319  args.getOrDefault<word>("write-format", getExtension(exportName))
320  );
321 
322 
323  // Check that reading/writing is supported
324  if (!hasReadWriteTypes(readFileType, writeFileType))
325  {
326  FatalError
327  << "Unsupported file format(s)" << nl
328  << exit(FatalError);
329  }
330 
331 
332  Info<< "Reading surf from " << importName << " ..." << nl
333  << "Writing surf to " << exportName << " ..." << endl;
334 
335 
336  meshedSurface surf1(importName, readFileType);
337 
338  pointField points(surf1.points());
339 
340 
341  // Begin operations
342 
343  // Input scaling
344  applyScaling(points, getScalingOpt("read-scale", args));
345 
346  vector v;
347  if (args.found("recentre"))
348  {
349  v = boundBox(points).centre();
350  Info<< "Adjust centre " << v << " -> (0 0 0)" << endl;
351  points -= v;
352  }
353 
354  if (args.readIfPresent("translate", v))
355  {
356  Info<< "Translating points by " << v << endl;
357  points += v;
358  }
359 
360  vector rotationCentre;
361  bool useRotationCentre = args.readIfPresent("centre", rotationCentre);
362  if (args.found("auto-centre") && !useRotationCentre)
363  {
364  useRotationCentre = true;
365  rotationCentre = boundBox(points).centre();
366  }
367 
368  if (useRotationCentre)
369  {
370  Info<< "Set centre of rotation to " << rotationCentre << endl;
371  points -= rotationCentre;
372  }
373 
374 
375  // Get a rotation specification
376 
377  tensor rot(Zero);
378  bool useRotation(false);
379 
380  if (args.found("rotate"))
381  {
382  Pair<vector> n1n2
383  (
384  args.lookup("rotate")()
385  );
386  n1n2[0].normalise();
387  n1n2[1].normalise();
388 
389  rot = rotationTensor(n1n2[0], n1n2[1]);
390  useRotation = true;
391  }
392  else if (args.found("rotate-angle"))
393  {
394  const Tuple2<vector, scalar> rotAxisAngle
395  (
396  args.lookup("rotate-angle")()
397  );
398 
399  const vector& axis = rotAxisAngle.first();
400  const scalar angle = rotAxisAngle.second();
401 
402  Info<< "Rotating points " << nl
403  << " about " << axis << nl
404  << " angle " << angle << nl;
405 
406  rot = axisAngle::rotation(axis, angle, true);
407  useRotation = true;
408  }
409  else if (args.found("rotate-x"))
410  {
411  const scalar angle = args.get<scalar>("rotate-x");
412 
413  Info<< "Rotating points about x-axis: " << angle << nl;
414 
415  rot = axisAngle::rotation(vector::X, angle, true);
416  useRotation = true;
417  }
418  else if (args.found("rotate-y"))
419  {
420  const scalar angle = args.get<scalar>("rotate-y");
421 
422  Info<< "Rotating points about y-axis: " << angle << nl;
423 
424  rot = axisAngle::rotation(vector::Y, angle, true);
425  useRotation = true;
426  }
427  else if (args.found("rotate-z"))
428  {
429  const scalar angle = args.get<scalar>("rotate-z");
430 
431  Info<< "Rotating points about z-axis: " << angle << nl;
432 
433  rot = axisAngle::rotation(vector::Z, angle, true);
434  useRotation = true;
435  }
436  else if (args.readIfPresent("rollPitchYaw", v))
437  {
438  Info<< "Rotating points by" << nl
439  << " roll " << v.x() << nl
440  << " pitch " << v.y() << nl
441  << " yaw " << v.z() << nl;
442 
443  rot = euler::rotation(euler::eulerOrder::ROLL_PITCH_YAW, v, true);
444  useRotation = true;
445  }
446  else if (args.readIfPresent("yawPitchRoll", v))
447  {
448  Info<< "Rotating points by" << nl
449  << " yaw " << v.x() << nl
450  << " pitch " << v.y() << nl
451  << " roll " << v.z() << nl;
452 
453  rot = euler::rotation(euler::eulerOrder::YAW_PITCH_ROLL, v, true);
454  useRotation = true;
455  }
456 
457  if (useRotation)
458  {
459  Info<< "Rotating points by " << rot << endl;
460  transform(points, rot, points);
461  }
462 
463  if (useRotationCentre)
464  {
465  Info<< "Unset centre of rotation from " << rotationCentre << endl;
466  points += rotationCentre;
467  }
468 
469  // Output scaling
470  applyScaling(points, getScalingOpt("write-scale", args));
471 
472  // Conversion to cylindrical coords
473  if (args.found("cylToCart"))
474  {
475  vectorField n1n2(args.lookup("cylToCart")());
476  n1n2[1].normalise();
477  n1n2[2].normalise();
478 
479  cylindricalCS ccs
480  (
481  "ccs",
482  n1n2[0],
483  n1n2[1],
484  n1n2[2]
485  );
486 
487  points = ccs.globalPosition(points);
488  }
489 
490  surf1.movePoints(points);
491  surf1.write(exportName, writeFileType);
492 
493  Info<< "End\n" << endl;
494 
495  return 0;
496 }
497 
498 
499 // ************************************************************************* //
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:462
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
A class for handling file names.
Definition: fileName.H:72
A cylindrical coordinate system (r-theta-z). The coordinate system angle theta is always in radians...
Definition: cylindricalCS.H:67
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:153
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
bool equal(const T &a, const T &b)
Compare two values for equality.
Definition: label.H:164
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
bool empty() const noexcept
True if List is empty (ie, size() is zero)
Definition: UList.H:675
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
Definition: argList.C:374
static void noParallel()
Remove the parallel options.
Definition: argList.C:584
static void addOptionCompat(const word &optName, std::pair< const char *, int > compat)
Specify an alias for the option name.
Definition: argList.C:418
A bounding box defined in terms of min/max extrema points.
Definition: boundBox.H:63
T getOrDefault(const word &optName, const T &deflt) const
Get a value from the named option if present, or return default.
Definition: argListI.H:300
tensor rotationTensor(const vector &n1, const vector &n2)
Rotational transformation tensor from vector n1 to n2.
Definition: transform.H:47
word ext() const
Return file name extension (part after last .)
Definition: wordI.H:171
Spatial transformation functions for primitive fields.
Namespace for coordinate system rotations.
Definition: axesRotation.C:30
An ordered pair of two objects of type <T> with first() and second() elements.
Definition: instant.H:46
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
const pointField & points
bool has_ext() const
Various checks for extensions.
Definition: stringI.H:43
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:130
A class for handling words, derived from Foam::string.
Definition: word.H:63
Extract command arguments and options from the supplied argc and argv parameters. ...
Definition: argList.H:118
static void addOption(const word &optName, const string &param="", const string &usage="", bool advanced=false)
Add an option to validOptions with usage information.
Definition: argList.C:385
ITstream lookup(const word &optName) const
Return an input stream from the named option.
Definition: argListI.H:177
dimensioned< Type > cmptMultiply(const dimensioned< Type > &, const dimensioned< Type > &)
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
bool readListIfPresent(const word &optName, List< T > &list) const
If named option is present, get a List of values treating a single entry like a list of size 1...
Definition: argListI.H:387
static bool canWriteType(const word &fileType, bool verbose=false)
Can we write this file format? Also checks proxy types.
Definition: MeshedSurface.C:70
T get(const label index) const
Get a value from the argument at index.
Definition: argListI.H:271
static tensor rotation(const vector &angles, bool degrees=false)
Rotation tensor calculated for the intrinsic Euler angles in z-x-z order.
static bool canReadType(const word &fileType, bool verbose=false)
Can we read this file format? Also checks friend types.
Definition: MeshedSurface.C:53
PtrList< volScalarField > & Y
static tensor rotation(const vector &axis, const scalar angle, bool degrees=false)
The rotation tensor for given axis/angle.
static void addArgument(const string &argName, const string &usage="")
Append a (mandatory) argument to validArgs.
Definition: argList.C:351
messageStream Info
Information stream (stdout output on master, null elsewhere)
bool readIfPresent(const word &optName, T &val) const
Read a value from the named option if present.
Definition: argListI.H:316
point centre() const
The centre (midpoint) of the bounding box.
Definition: boundBoxI.H:186
dimensionSet transform(const dimensionSet &ds)
Return the argument; transformations do not change the dimensions.
Definition: dimensionSet.C:521
Foam::argList args(argc, argv)
Tensor of scalars, i.e. Tensor<scalar>.
label count(const UList< word > &optionNames) const
Return how many of the specified options were used.
Definition: argList.C:2185
bool found(const word &optName) const
Return true if the named option is found.
Definition: argListI.H:171
Namespace for OpenFOAM.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127