Visual Computing Library
Loading...
Searching...
No Matches
save.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_LOAD_SAVE_STL_SAVE_H
24#define VCL_LOAD_SAVE_STL_SAVE_H
25
26#include <vclib/algorithms/core/polygon.h>
27#include <vclib/io/write.h>
28#include <vclib/load_save/settings.h>
29#include <vclib/misc/logger.h>
30#include <vclib/space/complex/mesh_info.h>
31
32namespace vcl {
33
34namespace detail {
35
36inline void writeStlHeader(std::ostream& fp, const SaveSettings& settings)
37{
38 std::string header = "solid STL generated by VCLib";
39 if (settings.binary) {
40 if (settings.magicsMode) {
41 std::string p;
42 for (uint i = 0; i < 3; i++)
43 p.push_back((char) 0x7f);
44 header = "COLOR=" + p + " MATERIAL=" + p + " " + p + " " + p;
45 }
46 while (header.size() < 80)
47 header.push_back(' ');
48 }
49
50 fp << header;
51 if (!settings.binary)
52 fp << std::endl;
53}
54
55template<Point3Concept PointType, Point3Concept NormalType>
56void writeSTLTriangle(
57 std::ostream& fp,
58 const PointType& p0,
59 const PointType& p1,
60 const PointType& p2,
61 const NormalType& n,
62 uint attributes,
63 const SaveSettings& settings)
64{
65 if (settings.binary) {
66 for (uint i = 0; i < 3; ++i)
67 io::writeFloat(fp, n[i]);
68
69 for (uint i = 0; i < 3; ++i)
70 io::writeFloat(fp, p0[i]);
71
72 for (uint i = 0; i < 3; ++i)
73 io::writeFloat(fp, p1[i]);
74
75 for (uint i = 0; i < 3; ++i)
76 io::writeFloat(fp, p2[i]);
77
78 io::writeUShort(fp, attributes);
79 }
80 else {
81 fp << " facet normal " << n.x() << " " << n.y() << " " << n.z()
82 << std::endl;
83 fp << " outer loop" << std::endl;
84
85 fp << " vertex " << p0.x() << " " << p0.y() << " " << p0.z()
86 << std::endl;
87 fp << " vertex " << p1.x() << " " << p1.y() << " " << p1.z()
88 << std::endl;
89 fp << " vertex " << p2.x() << " " << p2.y() << " " << p2.z()
90 << std::endl;
91
92 fp << " endloop" << std::endl;
93 fp << " endfacet" << std::endl;
94 }
95}
96
97} // namespace detail
98
99template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
100void saveStl(
101 const MeshType& m,
102 std::ostream& fp,
103 LogType& log = nullLogger,
104 const SaveSettings& settings = SaveSettings())
105{
106 MeshInfo meshInfo(m);
107
108 // make sure that the given info contains only components that are actually
109 // available in the mesh. meshInfo will contain the intersection between the
110 // components that the user wants to save and the components that are
111 // available in the mesh.
112 if (!settings.info.isEmpty())
113 meshInfo = settings.info.intersect(meshInfo);
114
115 log.log(0, "Saving STL file");
116
117 detail::writeStlHeader(fp, settings);
118
119 if constexpr (HasFaces<MeshType>) {
120 using FaceType = MeshType::FaceType;
121
122 if (settings.binary) {
123 io::writeInt(fp, m.faceNumber());
124 }
125
126 log.startProgress("Saving STL file", m.faceNumber());
127
128 for (const FaceType& f : m.faces()) {
129 // For each triangle write the normal, the three coords and a short
130 auto n = faceNormal(f);
131
132 unsigned short attributes = 0;
133
134 if constexpr (HasPerFaceColor<MeshType>) {
135 if (meshInfo.hasFaceColors()) {
136 if (settings.magicsMode)
137 attributes = 32768 | f.color().bgr5();
138 else
139 attributes = 32768 | f.color().rgb5();
140 }
141 }
142
143 if (f.vertexNumber() == 3) {
144 detail::writeSTLTriangle(
145 fp,
146 f.vertex(0)->coord(),
147 f.vertex(1)->coord(),
148 f.vertex(2)->coord(),
149 n,
150 attributes,
151 settings);
152 }
153 else {
154 std::vector<uint> tris = earCut(f);
155 for (uint i = 0; i < tris.size(); i += 3) {
156 detail::writeSTLTriangle(
157 fp,
158 f.vertex(tris[i])->coord(),
159 f.vertex(tris[i + 1])->coord(),
160 f.vertex(tris[i + 2])->coord(),
161 n,
162 attributes,
163 settings);
164 }
165 }
166
167 log.progress(m.index(f));
168 }
169
170 log.endProgress();
171 }
172
173 if (!settings.binary) {
174 fp << "endsolid VCLib" << std::endl;
175 }
176}
177
178template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
179void saveStl(
180 const MeshType& m,
181 std::ostream& fp,
182 const SaveSettings& settings,
183 LogType& log = nullLogger)
184{
185 saveStl(m, fp, log, settings);
186}
187
188template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
189void saveStl(
190 const MeshType& m,
191 const std::string& filename,
192 LogType& log = nullLogger,
193 const SaveSettings& settings = SaveSettings())
194{
195 std::ofstream fp = openOutputFileStream(filename, "stl");
196
197 saveStl(m, fp, log, settings);
198}
199
200template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
201void saveStl(
202 const MeshType& m,
203 const std::string& filename,
204 const SaveSettings& settings,
205 LogType& log = nullLogger)
206{
207 saveStl(m, filename, log, settings);
208}
209
210} // namespace vcl
211
212#endif // VCL_LOAD_SAVE_STL_SAVE_H
FaceType::VertexType::CoordType faceNormal(const FaceType &f)
Computes the normal of a face, without modifying the face. Works both for triangle and polygonal face...
Definition geometry.h:45
std::vector< uint > earCut(Iterator begin, Iterator end)
Triangulates a simple polygon with no holes using the ear-cutting algorithm.
Definition ear_cut.h:92
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:125
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:52