OBJsurfaceFormat.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) 2011-2025 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 "OBJsurfaceFormat.H"
27 #include "clock.H"
28 #include "IFstream.H"
29 #include "IStringStream.H"
30 #include "Ostream.H"
31 #include "OFstream.H"
32 #include "ListOps.H"
33 
34 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
35 
36 template<class Face>
38 (
39  const fileName& filename
40 )
41 {
42  read(filename);
43 }
44 
45 
46 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
47 
48 template<class Face>
50 (
51  const fileName& filename
52 )
53 {
54  const bool mustTriangulate = this->isTri();
55  this->clear();
56 
57  IFstream is(filename);
58  if (!is.good())
59  {
61  << "Cannot read file " << filename
62  << exit(FatalError);
63  }
64 
65  // assume that the groups are not intermixed
66  bool sorted = true;
67 
68  DynamicList<point> dynPoints;
69  DynamicList<Face> dynFaces;
70  DynamicList<label> dynZones;
71  DynamicList<word> dynNames;
72  DynamicList<label> dynSizes;
74 
75  // place faces without a group in zone0
76  label zoneI = 0;
77  lookup.insert("zone0", zoneI);
78  dynNames.append("zone0");
79  dynSizes.append(0);
80 
81  while (is.good())
82  {
83  const string line = this->getLineNoComment(is);
84 
85  // Read first word
86  IStringStream lineStream(line);
87  word cmd;
88  lineStream >> cmd;
89 
90  if (cmd == "v")
91  {
92  scalar x, y, z;
93  lineStream >> x >> y >> z;
94  dynPoints.append(point(x, y, z));
95  }
96  else if (cmd == "g")
97  {
98  word name;
99  lineStream >> name;
100 
102  if (fnd != lookup.end())
103  {
104  if (zoneI != fnd())
105  {
106  // group appeared out of order
107  sorted = false;
108  }
109  zoneI = fnd();
110  }
111  else
112  {
113  zoneI = dynSizes.size();
114  lookup.insert(name, zoneI);
115  dynNames.append(name);
116  dynSizes.append(0);
117  }
118  }
119  else if (cmd == "f")
120  {
121  DynamicList<label> dynVertices;
122 
123  // Assume 'f' is followed by space.
124  string::size_type endNum = 1;
125 
126  while (true)
127  {
128  string::size_type startNum =
129  line.find_first_not_of(' ', endNum);
130 
131  if (startNum == string::npos)
132  {
133  break;
134  }
135 
136  endNum = line.find(' ', startNum);
137 
138  string vertexSpec;
139  if (endNum != string::npos)
140  {
141  vertexSpec = line.substr(startNum, endNum-startNum);
142  }
143  else
144  {
145  vertexSpec = line.substr(startNum, line.size() - startNum);
146  }
147 
148  string::size_type slashPos = vertexSpec.find('/');
149 
150  label vertI = 0;
151  if (slashPos != string::npos)
152  {
153  IStringStream intStream(vertexSpec.substr(0, slashPos));
154 
155  intStream >> vertI;
156  }
157  else
158  {
159  IStringStream intStream(vertexSpec);
160 
161  intStream >> vertI;
162  }
163  dynVertices.append(vertI - 1);
164  }
165  dynVertices.shrink();
166 
167  labelUList& f = static_cast<labelUList&>(dynVertices);
168 
169  if (mustTriangulate && f.size() > 3)
170  {
171  // simple face triangulation about f[0]
172  // points may be incomplete
173  for (label fp1 = 1; fp1 < f.size() - 1; fp1++)
174  {
175  label fp2 = f.fcIndex(fp1);
176 
177  dynFaces.append(triFace(f[0], f[fp1], f[fp2]));
178  dynZones.append(zoneI);
179  dynSizes[zoneI]++;
180  }
181  }
182  else
183  {
184  dynFaces.append(Face(f));
185  dynZones.append(zoneI);
186  dynSizes[zoneI]++;
187  }
188  }
189  }
190 
191 
192  // transfer to normal lists
193  this->storedPoints().transfer(dynPoints);
194 
195  this->sortFacesAndStore(move(dynFaces), move(dynZones), sorted);
196 
197  // add zones, culling empty ones
198  this->addZones(dynSizes, dynNames, true);
199  return true;
200 }
201 
202 
203 template<class Face>
205 (
206  const fileName& filename,
207  const MeshedSurfaceProxy<Face>& surf
208 )
209 {
210  const pointField& pointLst = surf.points();
211  const List<Face>& faceLst = surf.faces();
212  const List<label>& faceMap = surf.faceMap();
213 
214  // for no zones, suppress the group name
215  const List<surfZone>& zones =
216  (
217  surf.surfZones().empty()
218  ? surfaceFormatsCore::oneZone(faceLst, "")
219  : surf.surfZones()
220  );
221 
222  const bool useFaceMap = (surf.useFaceMap() && zones.size() > 1);
223 
224  OFstream os(filename);
225  if (!os.good())
226  {
228  << "Cannot open file for writing " << filename
229  << exit(FatalError);
230  }
231 
232 
233  os << "# Wavefront OBJ file written " << clock::dateTime().c_str() << nl
234  << "o " << os.name().lessExt().name() << nl
235  << nl
236  << "# points : " << pointLst.size() << nl
237  << "# faces : " << faceLst.size() << nl
238  << "# zones : " << zones.size() << nl;
239 
240  // Print zone names as comment
241  forAll(zones, zoneI)
242  {
243  os << "# " << zoneI << " " << zones[zoneI].name()
244  << " (nFaces: " << zones[zoneI].size() << ")" << nl;
245  }
246 
247  os << nl
248  << "# <points count=\"" << pointLst.size() << "\">" << nl;
249 
250  // Write vertex coords
251  forAll(pointLst, ptI)
252  {
253  const point& pt = pointLst[ptI];
254 
255  os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << nl;
256  }
257 
258  os << "# </points>" << nl
259  << nl
260  << "# <faces count=\"" << faceLst.size() << "\">" << endl;
261 
262 
263  label faceIndex = 0;
264  forAll(zones, zoneI)
265  {
266  const surfZone& zone = zones[zoneI];
267 
268  if (zone.name().size())
269  {
270  os << "g " << zone.name() << endl;
271  }
272 
273  if (useFaceMap)
274  {
275  forAll(zone, localFacei)
276  {
277  const Face& f = faceLst[faceMap[faceIndex++]];
278 
279  os << 'f';
280  forAll(f, fp)
281  {
282  os << ' ' << f[fp] + 1;
283  }
284  os << endl;
285  }
286  }
287  else
288  {
289  forAll(zone, localFacei)
290  {
291  const Face& f = faceLst[faceIndex++];
292 
293  os << 'f';
294  forAll(f, fp)
295  {
296  os << ' ' << f[fp] + 1;
297  }
298  os << endl;
299  }
300  }
301  }
302  os << "# </faces>" << endl;
303 }
304 
305 
306 // ************************************************************************* //
scalar y
Various functions to operate on Lists.
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:73
#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
void transfer(List< T > &)
Transfer contents of the argument List into this.
Definition: DynamicListI.H:285
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
DynamicList< T, SizeInc, SizeMult, SizeDiv > & shrink()
Shrink the allocated space to the number of elements used.
Definition: DynamicListI.H:252
Input from file stream.
Definition: IFstream.H:85
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:333
Input from memory buffer stream.
Definition: IStringStream.H:52
bool good() const
Return true if next operation might succeed.
Definition: Istream.H:101
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
A proxy for writing MeshedSurface, UnsortedMeshedSurface and surfMesh to various file formats.
const List< surfZone > & surfZones() const
Const access to the surface zones.
const List< Face > & faces() const
Return const access to the faces.
bool useFaceMap() const
Use faceMap?
const List< label > & faceMap() const
Const access to the faceMap, zero-sized when unused.
const pointField & points() const
Return const access to the points.
Output to file stream.
Definition: OFstream.H:87
const fileName & name() const
Return the name of the stream.
Definition: OFstream.H:121
const Cmpt & z() const
Definition: VectorI.H:87
const Cmpt & y() const
Definition: VectorI.H:81
const Cmpt & x() const
Definition: VectorI.H:75
static void write(const fileName &, const MeshedSurfaceProxy< Face > &)
Write surface mesh components by proxy.
virtual bool read(const fileName &)
Read from file.
OBJsurfaceFormat(const fileName &)
Construct from file name.
A class for handling file names.
Definition: fileName.H:82
word name() const
Return file name (part beyond last /)
Definition: fileName.C:195
fileName lessExt() const
Return file name without extension (part before last .)
Definition: fileName.C:303
A line primitive.
Definition: line.H:71
const word & name() const
Return name.
A surface zone on a MeshedSurface.
Definition: surfZone.H:65
A class for handling words, derived from string.
Definition: word.H:63
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
tUEqn clear()
void read(Istream &, label &, const dictionary &)
In-place read with dictionary lookup.
const unitSet & lookup(const word &unitName)
Lookup and return the named unit from the table.
Definition: units.C:346
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
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
vector point
Point is a vector.
Definition: point.H:41
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
error FatalError
static const char nl
Definition: Ostream.H:297
face triFace(3)
labelList f(nPoints)