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 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 
62 using namespace Foam;
63 using namespace Foam::coordinateRotations;
64 
65 static word getExtension(const fileName& name)
66 {
67  return
68  (
69  name.has_ext("gz")
70  ? name.stem().ext()
71  : name.ext()
72  );
73 }
74 
75 
76 // Non-short-circuiting check to get all warnings
77 static bool hasReadWriteTypes(const word& readType, const word& writeType)
78 {
79  volatile bool good = true;
80 
81  if (!meshedSurface::canReadType(readType, true))
82  {
83  good = false;
84  }
85 
86  if (!meshedSurface::canWriteType(writeType, true))
87  {
88  good = false;
89  }
90 
91  return good;
92 }
93 
94 
95 // Retrieve scaling option
96 // - size 0 : no scaling
97 // - size 1 : uniform scaling
98 // - size 3 : non-uniform scaling
99 List<scalar> getScalingOpt(const word& optName, const argList& args)
100 {
101  // readListIfPresent handles single or multiple values
102  // - accept 1 or 3 values
103 
104  List<scalar> scaling;
105  args.readListIfPresent(optName, scaling);
106 
107  if (scaling.size() == 1)
108  {
109  // Uniform scaling
110  }
111  else if (scaling.size() == 3)
112  {
113  // Non-uniform, but may actually be uniform
114  if
115  (
116  equal(scaling[0], scaling[1])
117  && equal(scaling[0], scaling[2])
118  )
119  {
120  scaling.resize(1);
121  }
122  }
123  else if (!scaling.empty())
124  {
125  FatalError
126  << "Incorrect number of components, must be 1 or 3." << nl
127  << " -" << optName << ' ' << args[optName].c_str() << endl
128  << exit(FatalError);
129  }
130 
131  if (scaling.size() == 1 && equal(scaling[0], 1))
132  {
133  // Scale factor 1 == no scaling
134  scaling.clear();
135  }
136 
137  // Zero and negative scaling are permitted
138 
139  return scaling;
140 }
141 
142 
143 void applyScaling(pointField& points, const List<scalar>& scaling)
144 {
145  if (scaling.size() == 1)
146  {
147  Info<< "Scaling points uniformly by " << scaling[0] << nl;
148  points *= scaling[0];
149  }
150  else if (scaling.size() == 3)
151  {
152  Info<< "Scaling points by ("
153  << scaling[0] << ' '
154  << scaling[1] << ' '
155  << scaling[2] << ')' << nl;
156 
157  points.replace(vector::X, scaling[0]*points.component(vector::X));
159  points.replace(vector::Z, scaling[2]*points.component(vector::Z));
160  }
161 }
162 
163 
164 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
165 
166 int main(int argc, char *argv[])
167 {
169  (
170  "Transform (translate / rotate / scale) surface points.\n"
171  "Like transformPoints but for surfaces.\n"
172  "Note: roll=rotate about x, pitch=rotate about y, yaw=rotate about z"
173  );
175  argList::addArgument("input", "The input surface file");
176  argList::addArgument("output", "The output surface file");
178  (
179  "recentre",
180  "Recentre the bounding box before other operations"
181  );
183  (
184  "translate",
185  "vector",
186  "Translate by specified <vector> before rotations"
187  );
189  (
190  "auto-centre",
191  "Use bounding box centre as centre for rotations"
192  );
194  (
195  "centre",
196  "point",
197  "Use specified <point> as centre for rotations"
198  );
199  argList::addOptionCompat("auto-centre", {"auto-origin", 2206});
200  argList::addOptionCompat("centre", {"origin", 2206});
201 
203  (
204  "rotate",
205  "(vectorA vectorB)",
206  "Rotate from <vectorA> to <vectorB> - eg, '((1 0 0) (0 0 1))'"
207  );
209  (
210  "rotate-angle",
211  "(vector angle)",
212  "Rotate <angle> degrees about <vector> - eg, '((1 0 0) 45)'"
213  );
215  (
216  "rotate-x", "deg",
217  "Rotate (degrees) about x-axis"
218  );
220  (
221  "rotate-y", "deg",
222  "Rotate (degrees) about y-axis"
223  );
225  (
226  "rotate-z", "deg",
227  "Rotate (degrees) about z-axis"
228  );
230  (
231  "rollPitchYaw",
232  "vector",
233  "Rotate by '(roll pitch yaw)' degrees"
234  );
236  (
237  "yawPitchRoll",
238  "vector",
239  "Rotate by '(yaw pitch roll)' degrees"
240  );
242  (
243  "read-scale",
244  "scalar | vector",
245  "Uniform or non-uniform input scaling"
246  );
248  (
249  "write-scale",
250  "scalar | vector",
251  "Uniform or non-uniform output scaling"
252  );
254  (
255  "read-format",
256  "type",
257  "Input format (default: use file extension)"
258  );
260  (
261  "write-format",
262  "type",
263  "Output format (default: use file extension)"
264  );
265 
266  // Backward compatibility and with transformPoints
267  argList::addOptionCompat("write-scale", {"scale", -2006});
268 
269  argList args(argc, argv);
270 
271  // Verify that an operation has been specified
272  {
273  const List<word> operationNames
274  ({
275  "recentre",
276  "translate",
277  "rotate",
278  "rotate-angle",
279  "rotate-x",
280  "rotate-y",
281  "rotate-z",
282  "rollPitchYaw",
283  "yawPitchRoll",
284  "read-scale",
285  "write-scale"
286  });
287 
288  if (!args.count(operationNames))
289  {
290  FatalError
291  << "No operation supplied, "
292  << "use at least one of the following:" << nl
293  << " ";
294 
295  for (const auto& opName : operationNames)
296  {
297  FatalError
298  << " -" << opName;
299  }
300 
301  FatalError
302  << nl << exit(FatalError);
303  }
304  }
305 
306  const auto importName = args.get<fileName>(1);
307  const auto exportName = args.get<fileName>(2);
308 
309  const word readFileType
310  (
311  args.getOrDefault<word>("read-format", getExtension(importName))
312  );
313 
314  const word writeFileType
315  (
316  args.getOrDefault<word>("write-format", getExtension(exportName))
317  );
318 
319 
320  // Check that reading/writing is supported
321  if (!hasReadWriteTypes(readFileType, writeFileType))
322  {
323  FatalError
324  << "Unsupported file format(s)" << nl
325  << exit(FatalError);
326  }
327 
328 
329  Info<< "Reading surf from " << importName << " ..." << nl
330  << "Writing surf to " << exportName << " ..." << endl;
331 
332 
333  meshedSurface surf1(importName, readFileType);
334 
335  pointField points(surf1.points());
336 
337 
338  // Begin operations
339 
340  // Input scaling
341  applyScaling(points, getScalingOpt("read-scale", args));
342 
343  vector v;
344  if (args.found("recentre"))
345  {
346  v = boundBox(points).centre();
347  Info<< "Adjust centre " << v << " -> (0 0 0)" << endl;
348  points -= v;
349  }
350 
351  if (args.readIfPresent("translate", v))
352  {
353  Info<< "Translating points by " << v << endl;
354  points += v;
355  }
356 
357  vector rotationCentre;
358  bool useRotationCentre = args.readIfPresent("centre", rotationCentre);
359  if (args.found("auto-centre") && !useRotationCentre)
360  {
361  useRotationCentre = true;
362  rotationCentre = boundBox(points).centre();
363  }
364 
365  if (useRotationCentre)
366  {
367  Info<< "Set centre of rotation to " << rotationCentre << endl;
368  points -= rotationCentre;
369  }
370 
371 
372  // Get a rotation specification
373 
374  tensor rot(Zero);
375  bool useRotation(false);
376 
377  if (args.found("rotate"))
378  {
379  Pair<vector> n1n2
380  (
381  args.lookup("rotate")()
382  );
383  n1n2[0].normalise();
384  n1n2[1].normalise();
385 
386  rot = rotationTensor(n1n2[0], n1n2[1]);
387  useRotation = true;
388  }
389  else if (args.found("rotate-angle"))
390  {
391  const Tuple2<vector, scalar> rotAxisAngle
392  (
393  args.lookup("rotate-angle")()
394  );
395 
396  const vector& axis = rotAxisAngle.first();
397  const scalar angle = rotAxisAngle.second();
398 
399  Info<< "Rotating points " << nl
400  << " about " << axis << nl
401  << " angle " << angle << nl;
402 
403  rot = axisAngle::rotation(axis, angle, true);
404  useRotation = true;
405  }
406  else if (args.found("rotate-x"))
407  {
408  const scalar angle = args.get<scalar>("rotate-x");
409 
410  Info<< "Rotating points about x-axis: " << angle << nl;
411 
412  rot = axisAngle::rotation(vector::X, angle, true);
413  useRotation = true;
414  }
415  else if (args.found("rotate-y"))
416  {
417  const scalar angle = args.get<scalar>("rotate-y");
418 
419  Info<< "Rotating points about y-axis: " << angle << nl;
420 
421  rot = axisAngle::rotation(vector::Y, angle, true);
422  useRotation = true;
423  }
424  else if (args.found("rotate-z"))
425  {
426  const scalar angle = args.get<scalar>("rotate-z");
427 
428  Info<< "Rotating points about z-axis: " << angle << nl;
429 
430  rot = axisAngle::rotation(vector::Z, angle, true);
431  useRotation = true;
432  }
433  else if (args.readIfPresent("rollPitchYaw", v))
434  {
435  Info<< "Rotating points by" << nl
436  << " roll " << v.x() << nl
437  << " pitch " << v.y() << nl
438  << " yaw " << v.z() << nl;
439 
440  rot = euler::rotation(euler::eulerOrder::ROLL_PITCH_YAW, v, true);
441  useRotation = true;
442  }
443  else if (args.readIfPresent("yawPitchRoll", v))
444  {
445  Info<< "Rotating points by" << nl
446  << " yaw " << v.x() << nl
447  << " pitch " << v.y() << nl
448  << " roll " << v.z() << nl;
449 
450  rot = euler::rotation(euler::eulerOrder::YAW_PITCH_ROLL, v, true);
451  useRotation = true;
452  }
453 
454  if (useRotation)
455  {
456  Info<< "Rotating points by " << rot << endl;
457  transform(points, rot, points);
458  }
459 
460  if (useRotationCentre)
461  {
462  Info<< "Unset centre of rotation from " << rotationCentre << endl;
463  points += rotationCentre;
464  }
465 
466  // Output scaling
467  applyScaling(points, getScalingOpt("write-scale", args));
468 
469  surf1.movePoints(points);
470  surf1.write(exportName, writeFileType);
471 
472  Info<< "End\n" << endl;
473 
474  return 0;
475 }
476 
477 
478 // ************************************************************************* //
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:453
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:118
A class for handling file names.
Definition: fileName.H:71
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:132
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:420
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
Definition: argList.C:365
static void noParallel()
Remove the parallel options.
Definition: argList.C:551
static void addOptionCompat(const word &optName, std::pair< const char *, int > compat)
Specify an alias for the option name.
Definition: argList.C:409
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
void replace(const direction, const UList< cmptType > &)
Replace a component field of the field.
Definition: Field.C:587
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 INVALID.
Definition: exprTraits.C:52
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:109
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:376
ITstream lookup(const word &optName) const
Return an input stream from the named option.
Definition: argListI.H:177
tmp< Field< cmptType > > component(const direction) const
Return a component field of the field.
Definition: Field.C:575
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
bool equal(const T &s1, const T &s2)
Compare two values for equality.
Definition: doubleFloat.H:41
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:342
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:179
dimensionSet transform(const dimensionSet &ds)
Return the argument; transformations do not change the dimensions.
Definition: dimensionSet.C:529
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:1843
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:157