Visual Computing Library  devel
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_IO_MESH_STL_SAVE_H
24#define VCL_IO_MESH_STL_SAVE_H
25
26#include <vclib/io/mesh/settings.h>
27#include <vclib/io/write.h>
28
29#include <vclib/algorithms/core.h>
30#include <vclib/space/complex.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 const SaveSettings& settings = SaveSettings(),
104 LogType& log = nullLogger)
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 3 vert positions and a
130 // short
131 auto n = faceNormal(f);
132
133 unsigned short attributes = 0;
134
135 if constexpr (HasPerFaceColor<MeshType>) {
136 if (meshInfo.hasPerFaceColor()) {
137 if (settings.magicsMode)
138 attributes = 32768 | f.color().bgr5();
139 else
140 attributes = 32768 | f.color().rgb5();
141 }
142 }
143
144 if (f.vertexNumber() == 3) {
145 detail::writeSTLTriangle(
146 fp,
147 f.vertex(0)->position(),
148 f.vertex(1)->position(),
149 f.vertex(2)->position(),
150 n,
151 attributes,
152 settings);
153 }
154 else {
155 std::vector<uint> tris = earCut(f);
156 for (uint i = 0; i < tris.size(); i += 3) {
157 detail::writeSTLTriangle(
158 fp,
159 f.vertex(tris[i])->position(),
160 f.vertex(tris[i + 1])->position(),
161 f.vertex(tris[i + 2])->position(),
162 n,
163 attributes,
164 settings);
165 }
166 }
167
168 log.progress(m.index(f));
169 }
170
171 log.endProgress();
172 }
173
174 if (!settings.binary) {
175 fp << "endsolid VCLib" << std::endl;
176 }
177}
178
179template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
180void saveStl(
181 const MeshType& m,
182 const std::string& filename,
183 const SaveSettings& settings = SaveSettings(),
184 LogType& log = nullLogger)
185{
186 std::ofstream fp = openOutputFileStream(filename, "stl");
187
188 saveStl(m, fp, settings, log);
189}
190
191} // namespace vcl
192
193#endif // VCL_IO_MESH_STL_SAVE_H
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:123
FaceType::VertexType::PositionType faceNormal(const FaceType &f)
Computes the normal of a face, without modifying the face. Works both for triangle and polygonal face...
Definition geometry.h:46
std::vector< uint > earCut(Iterator begin, Iterator end)
Triangulates a simple polygon with no holes using the ear-cutting algorithm.
Definition ear_cut.h:90
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:84