OBJsurfaceFormat.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) 2016-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 "OBJsurfaceFormat.H"
30#include "clock.H"
31#include "Fstream.H"
32#include "stringOps.H"
33#include "faceTraits.H"
34
35// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
36
37template<class Face>
39(
40 const fileName& filename
41)
42{
43 read(filename);
44}
45
46
47// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
48
49template<class Face>
51(
52 const fileName& filename
53)
54{
55 // Clear everything
56 this->clear();
57
58 IFstream is(filename);
59 if (!is.good())
60 {
62 << "Cannot read file " << filename << nl
63 << exit(FatalError);
64 }
65
66 // Assume that the groups are not intermixed
67 // Place faces without a group in zone0.
68 // zoneId = -1 to signal uninitialized
69 label zoneId = -1;
70 bool sorted = true;
71
72 DynamicList<point> dynPoints;
73 DynamicList<label> dynVerts;
74
75 DynamicList<label> dynElemId; // unused
76 DynamicList<Face> dynFaces;
77
78 DynamicList<word> dynNames;
79 DynamicList<label> dynZones;
80 DynamicList<label> dynSizes;
81
82 HashTable<label> groupLookup;
83
84 while (is.good())
85 {
86 string line = this->getLineNoComment(is);
87
88 // Line continuations
89 while (line.removeEnd('\\'))
90 {
91 line += this->getLineNoComment(is);
92 }
93
94 const SubStrings<string> tokens = stringOps::splitSpace(line);
95
96 // Require command and some arguments
97 if (tokens.size() < 2)
98 {
99 continue;
100 }
101
102 const word cmd = word::validate(tokens[0]);
103
104 if (cmd == "v")
105 {
106 // Vertex
107 // v x y z
108
109 dynPoints.append
110 (
111 point
112 (
113 readScalar(tokens[1]),
114 readScalar(tokens[2]),
115 readScalar(tokens[3])
116 )
117 );
118 }
119 else if (cmd == "g")
120 {
121 // Grouping
122 // g name
123
124 const word groupName = word::validate(tokens[1]);
125 const auto iterGroup = groupLookup.cfind(groupName);
126
127 if (iterGroup.found())
128 {
129 if (zoneId != *iterGroup)
130 {
131 sorted = false; // Group appeared out of order
132 }
133 zoneId = *iterGroup;
134 }
135 else
136 {
137 zoneId = dynSizes.size();
138 groupLookup.insert(groupName, zoneId);
139 dynNames.append(groupName);
140 dynSizes.append(0);
141 }
142 }
143 else if (cmd == "f")
144 {
145 // Face
146 // f v1 v2 v3 ...
147 // OR
148 // f v1/vt1 v2/vt2 v3/vt3 ...
149
150 // Ensure it has as valid grouping
151 if (zoneId < 0)
152 {
153 zoneId = 0;
154 groupLookup.insert("zone0", 0);
155 dynNames.append("zone0");
156 dynSizes.append(0);
157 }
158
159 dynVerts.clear();
160
161 bool first = true;
162 for (const auto& tok : tokens)
163 {
164 if (first)
165 {
166 // skip initial "f" or "l"
167 first = false;
168 continue;
169 }
170
171 const string vrtSpec(tok);
172 const auto slash = vrtSpec.find('/');
173
174 const label vertId =
175 (
176 slash != string::npos
177 ? readLabel(vrtSpec.substr(0, slash))
178 : readLabel(vrtSpec)
179 );
180
181 dynVerts.append(vertId - 1);
182 }
183
184 const labelUList& f = dynVerts;
185
186 if (faceTraits<Face>::isTri() && f.size() > 3)
187 {
188 // simple face triangulation about f[0]
189 // points may be incomplete
190 for (label fp1 = 1; fp1 < f.size() - 1; fp1++)
191 {
192 const label fp2 = f.fcIndex(fp1);
193
194 dynFaces.append(Face{f[0], f[fp1], f[fp2]});
195 dynZones.append(zoneId);
196 dynSizes[zoneId]++;
197 }
198 }
199 else if (f.size() >= 3)
200 {
201 dynFaces.append(Face(f));
202 dynZones.append(zoneId);
203 dynSizes[zoneId]++;
204 }
205 }
206 }
207
208
209 // Transfer to normal lists
210 this->storedPoints().transfer(dynPoints);
211
212 this->sortFacesAndStore(dynFaces, dynZones, dynElemId, sorted);
213
214 // Add zones (retaining empty ones)
215 this->addZones(dynSizes, dynNames);
216 this->addZonesToFaces(); // for labelledTri
217
218 return true;
219}
220
221
222template<class Face>
224(
225 const fileName& filename,
226 const MeshedSurfaceProxy<Face>& surf,
227 IOstreamOption streamOpt,
228 const dictionary&
229)
230{
231 // ASCII only, allow output compression
232 streamOpt.format(IOstream::ASCII);
233
234 const UList<point>& pointLst = surf.points();
235 const UList<Face>& faceLst = surf.surfFaces();
236 const UList<label>& faceMap = surf.faceMap();
237
238 // for no zones, suppress the group name
239 const surfZoneList zones =
240 (
241 surf.surfZones().empty()
242 ? surfaceFormatsCore::oneZone(faceLst, "")
243 : surf.surfZones()
244 );
245
246 const bool useFaceMap = (surf.useFaceMap() && zones.size() > 1);
247
248 OFstream os(filename, streamOpt);
249 if (!os.good())
250 {
252 << "Cannot write file " << filename << nl
253 << exit(FatalError);
254 }
255
256
257 os << "# Wavefront OBJ file written " << clock::dateTime().c_str() << nl
258 << "o " << os.name().nameLessExt() << nl
259 << nl
260 << "# points : " << pointLst.size() << nl
261 << "# faces : " << faceLst.size() << nl
262 << "# zones : " << zones.size() << nl;
263
264 // Print zone names as comment
265 forAll(zones, zonei)
266 {
267 os << "# " << zonei << " " << zones[zonei].name()
268 << " (nFaces: " << zones[zonei].size() << ")" << nl;
269 }
270
271 os << nl
272 << "# <points count=\"" << pointLst.size() << "\">" << nl;
273
274 // Write vertex coords
275 for (const point& pt : pointLst)
276 {
277 os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << nl;
278 }
279
280 os << "# </points>" << nl
281 << nl
282 << "# <faces count=\"" << faceLst.size() << "\">" << nl;
283
284
285 label faceIndex = 0;
286
287 for (const surfZone& zone : zones)
288 {
289 if (zone.name().size())
290 {
291 os << "g " << zone.name() << nl;
292 }
293
294 for (label nLocal = zone.size(); nLocal--; ++faceIndex)
295 {
296 const label facei =
297 (useFaceMap ? faceMap[faceIndex] : faceIndex);
298
299 const Face& f = faceLst[facei];
300
301 os << 'f';
302 for (const label verti : f)
303 {
304 os << ' ' << (verti + 1);
305 }
306 os << nl;
307 }
308 }
309 os << "# </faces>" << nl;
310}
311
312
313// ************************************************************************* //
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:72
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:391
void transfer(List< T > &list)
Transfer contents of the argument List into this.
Definition: DynamicListI.H:467
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:503
A HashTable similar to std::unordered_map.
Definition: HashTable.H:123
const_iterator cfind(const Key &key) const
Find and return an const_iterator set at the hashed entry.
Definition: HashTableI.H:141
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:180
Input from file stream, using an ISstream.
Definition: IFstream.H:57
The IOstreamOption is a simple container for options an IOstream can normally have.
streamFormat format() const noexcept
Get the current stream format.
bool good() const noexcept
True if next operation might succeed.
Definition: IOstream.H:233
A proxy for writing MeshedSurface, UnsortedMeshedSurface and surfMesh to various file formats.
const UList< surfZone > & surfZones() const
Const access to the surface zones.
const UList< Face > & surfFaces() const
Return const access to the faces.
bool useFaceMap() const
Can/should use faceMap?
const pointField & points() const
Return const access to the points.
const labelUList & faceMap() const
Const access to the faceMap, zero-sized when unused.
Output to file stream, using an OSstream.
Definition: OFstream.H:57
virtual bool read()
Re-read model coefficients if they have changed.
Sub-ranges of a string with a structure similar to std::match_results, but without the underlying reg...
Definition: SubStrings.H:54
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:427
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
Traits class for faces.
Definition: faceTraits.H:51
Read/write Alias/Wavefront OBJ format.
A class for handling file names.
Definition: fileName.H:76
virtual bool write()
Write the output fields.
A line primitive.
Definition: line.H:68
A surface zone on a MeshedSurface.
Definition: surfZone.H:59
A class for handling words, derived from Foam::string.
Definition: word.H:68
const word & name() const noexcept
The zone name.
Base class for mesh zones.
Definition: zone.H:67
patchWriters clear()
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
OBJstream os(runTime.globalPath()/outputName)
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
label readLabel(const char *buf)
Parse entire buffer as a label, skipping leading/trailing whitespace.
Definition: label.H:66
error FatalError
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
labelList f(nPoints)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333