PLYwoot
Header-only C++17 library for parsing and writing PLY files
Loading...
Searching...
No Matches
writer.hpp
Go to the documentation of this file.
1/*
2 This file is part of PLYwoot, a header-only PLY parser.
3
4 Copyright (C) 2023-2025, Ton van den Heuvel
5
6 PLYwoot is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#ifndef PLYWOOT_WRITER_HPP
21#define PLYWOOT_WRITER_HPP
22
24
25#include "reflect.hpp"
26#include "std.hpp"
27#include "types.hpp"
28
29#include <cstdint>
30#include <type_traits>
31
32namespace plywoot::detail {
33
46template<typename FormatWriterPolicy>
47class Writer : private FormatWriterPolicy
48{
49public:
50 using FormatWriterPolicy::FormatWriterPolicy;
51
55 void write(const PlyElement &element, const std::uint8_t *src, std::size_t alignment) const
56 {
57 for (std::size_t i = 0; i < element.size(); ++i)
58 {
59 const std::vector<PlyProperty> &properties = element.properties();
60 const auto first = properties.begin();
61 const auto last = properties.end();
62
63 auto curr = first;
64 while (curr < last)
65 {
66 if (curr > first) { this->writeTokenSeparator(); }
67
68 const PlyProperty &property = *curr++;
69 if (property.isList())
70 {
71 switch (property.type())
72 {
73 case PlyDataType::Char:
74 src = writeProperty(src, property, reflect::Type<std::vector<char>>{});
75 break;
76 case PlyDataType::UChar:
77 src = writeProperty(src, property, reflect::Type<std::vector<unsigned char>>{});
78 break;
79 case PlyDataType::Short:
80 src = writeProperty(src, property, reflect::Type<std::vector<short>>{});
81 break;
82 case PlyDataType::UShort:
83 src = writeProperty(src, property, reflect::Type<std::vector<unsigned short>>{});
84 break;
85 case PlyDataType::Int:
86 src = writeProperty(src, property, reflect::Type<std::vector<int>>{});
87 break;
88 case PlyDataType::UInt:
89 src = writeProperty(src, property, reflect::Type<std::vector<unsigned int>>{});
90 break;
91 case PlyDataType::Float:
92 src = writeProperty(src, property, reflect::Type<std::vector<float>>{});
93 break;
94 case PlyDataType::Double:
95 src = writeProperty(src, property, reflect::Type<std::vector<double>>{});
96 break;
97 }
98 }
99 else
100 {
101 switch (property.type())
102 {
103 case PlyDataType::Char:
104 src = writeProperty(src, property, reflect::Type<char>{});
105 break;
106 case PlyDataType::UChar:
107 src = writeProperty(src, property, reflect::Type<unsigned char>{});
108 break;
109 case PlyDataType::Short:
110 src = writeProperty(src, property, reflect::Type<short>{});
111 break;
112 case PlyDataType::UShort:
113 src = writeProperty(src, property, reflect::Type<unsigned short>{});
114 break;
115 case PlyDataType::Int:
116 src = writeProperty(src, property, reflect::Type<int>{});
117 break;
118 case PlyDataType::UInt:
119 src = writeProperty(src, property, reflect::Type<unsigned int>{});
120 break;
121 case PlyDataType::Float:
122 src = writeProperty(src, property, reflect::Type<float>{});
123 break;
124 case PlyDataType::Double:
125 src = writeProperty(src, property, reflect::Type<double>{});
126 break;
127 }
128 }
129 }
130
131 src = detail::align(src, alignment);
132
133 this->writeNewline();
134 }
135 }
136
137 template<typename... Ts>
138 void write(const PlyElement &element, const reflect::Layout<Ts...> layout) const
139 {
140 const auto first = element.properties().begin();
141 const auto last = element.properties().end();
142
143 const std::uint8_t *src = layout.data();
144 for (std::size_t i = 0; i < layout.size(); ++i)
145 {
146 src = detail::align(writeElement<Ts...>(src, first, last), layout.alignment());
147 }
148 }
149
150private:
151 template<typename PlyT, typename SrcT>
152 const std::uint8_t *writeProperty(const std::uint8_t *src, reflect::Type<SrcT>) const
153 {
154 if constexpr (std::is_arithmetic_v<SrcT>)
155 {
156 src = static_cast<const std::uint8_t *>(detail::align(src, alignof(SrcT)));
157 this->writeNumber(static_cast<PlyT>(*reinterpret_cast<const SrcT *>(src)));
158 return src + sizeof(SrcT);
159 }
160 else { return static_cast<const std::uint8_t *>(detail::align(src, alignof(SrcT))) + sizeof(SrcT); }
161 }
162
165 template<typename PlyT, typename SrcT, std::size_t N>
166 const std::uint8_t *writeProperty(const std::uint8_t *src, reflect::Type<reflect::Pack<SrcT, N>>) const
167 {
168 static_assert(N > 0, "invalid pack size specified (needs to be larger than zero)");
169 static_assert(
170 std::is_arithmetic<SrcT>::value, "it is not (yet) possible to write packs of non-numeric types");
171
172 src = static_cast<const std::uint8_t *>(detail::align(src, alignof(SrcT)));
173 this->template writeNumbers<PlyT, SrcT>(reinterpret_cast<const SrcT *>(src), N);
174 return src + N * sizeof(SrcT);
175 }
176
179 template<typename SrcT>
180 const std::uint8_t *writeProperty(const std::uint8_t *src, reflect::Type<reflect::Stride<SrcT>>) const
181 {
182 return static_cast<const std::uint8_t *>(detail::align(src, alignof(SrcT))) + sizeof(SrcT);
183 }
184
187 template<typename PlyT, typename PlySizeT, typename SrcT, std::size_t N>
188 const std::uint8_t *writeListProperty(const std::uint8_t *src, reflect::Type<reflect::Array<SrcT, N>>) const
189 {
190 static_assert(N > 0, "invalid array size specified (needs to be larger than zero)");
191
192 src = static_cast<const std::uint8_t *>(detail::align(src, alignof(SrcT)));
193 this->template writeList<PlySizeT, PlyT, SrcT>(reinterpret_cast<const SrcT *>(src), N);
194 return src + N * sizeof(SrcT);
195 }
196
198 template<typename PlyT, typename PlySizeT, typename SrcT>
199 const std::uint8_t *writeListProperty(const std::uint8_t *src, reflect::Type<std::vector<SrcT>>) const
200 {
201 src = static_cast<const std::uint8_t *>(detail::align(src, alignof(std::vector<SrcT>)));
202 const std::vector<SrcT> &v = *reinterpret_cast<const std::vector<SrcT> *>(src);
203 this->template writeList<PlySizeT, PlyT, SrcT>(v.data(), v.size());
204 return src + sizeof(std::vector<SrcT>);
205 }
206
207 template<typename PlyT, typename TypeTag>
208 const std::uint8_t *writeListProperty(const std::uint8_t *src, const PlyProperty &property, TypeTag tag)
209 const
210 {
211 switch (property.sizeType())
212 {
213 case PlyDataType::Char:
214 return writeListProperty<PlyT, char>(src, tag);
215 case PlyDataType::UChar:
216 return writeListProperty<PlyT, unsigned char>(src, tag);
217 case PlyDataType::Short:
218 return writeListProperty<PlyT, short>(src, tag);
219 case PlyDataType::UShort:
220 return writeListProperty<PlyT, unsigned short>(src, tag);
221 case PlyDataType::Int:
222 return writeListProperty<PlyT, int>(src, tag);
223 case PlyDataType::UInt:
224 return writeListProperty<PlyT, unsigned int>(src, tag);
225 case PlyDataType::Float:
226 return writeListProperty<PlyT, float>(src, tag);
227 case PlyDataType::Double:
228 return writeListProperty<PlyT, double>(src, tag);
229 }
230
231 return src;
232 }
233
234 template<typename TypeTag>
235 const std::uint8_t *writeProperty(const std::uint8_t *src, const PlyProperty &property, TypeTag tag) const
236 {
237 if constexpr (detail::isList<TypeTag>())
238 {
239 switch (property.type())
240 {
241 case PlyDataType::Char:
242 return writeListProperty<char>(src, property, tag);
243 case PlyDataType::UChar:
244 return writeListProperty<unsigned char>(src, property, tag);
245 case PlyDataType::Short:
246 return writeListProperty<short>(src, property, tag);
247 case PlyDataType::UShort:
248 return writeListProperty<unsigned short>(src, property, tag);
249 case PlyDataType::Int:
250 return writeListProperty<int>(src, property, tag);
251 case PlyDataType::UInt:
252 return writeListProperty<unsigned int>(src, property, tag);
253 case PlyDataType::Float:
254 return writeListProperty<float>(src, property, tag);
255 case PlyDataType::Double:
256 return writeListProperty<double>(src, property, tag);
257 }
258 }
259 else
260 {
261 switch (property.type())
262 {
263 case PlyDataType::Char:
264 return writeProperty<char>(src, tag);
265 case PlyDataType::UChar:
266 return writeProperty<unsigned char>(src, tag);
267 case PlyDataType::Short:
268 return writeProperty<short>(src, tag);
269 case PlyDataType::UShort:
270 return writeProperty<unsigned short>(src, tag);
271 case PlyDataType::Int:
272 return writeProperty<int>(src, tag);
273 case PlyDataType::UInt:
274 return writeProperty<unsigned int>(src, tag);
275 case PlyDataType::Float:
276 return writeProperty<float>(src, tag);
277 case PlyDataType::Double:
278 return writeProperty<double>(src, tag);
279 }
280 }
281
282 return src;
283 }
284
289 template<typename SrcT>
290 const std::uint8_t *writeProperty(
291 const std::uint8_t *src,
292 PlyPropertyConstIterator first,
293 PlyPropertyConstIterator last) const
294 {
295 return first < last ? writeProperty(src, *first, reflect::Type<SrcT>{})
296 : writeProperty(src, reflect::Type<reflect::Stride<SrcT>>{});
297 }
298
304 template<typename Policy, typename T>
305 const std::uint8_t *writeProperties(
306 const std::uint8_t *src,
307 PlyPropertyConstIterator first,
308 PlyPropertyConstIterator last) const
309 {
310 return writeProperty<T>(src, first, last);
311 }
312
313 template<typename Policy, typename T, typename U, typename... Ts>
314 const std::uint8_t *writeProperties(
315 const std::uint8_t *src,
316 PlyPropertyConstIterator first,
317 PlyPropertyConstIterator last) const
318 {
319 if constexpr (std::is_same_v<Policy, AsciiWriterPolicy>)
320 {
321 src = writeProperty<T>(src, first, last);
322 first += detail::numProperties<T>();
323 if (first < last) { this->writeTokenSeparator(); }
324 return writeProperties<Policy, U, Ts...>(src, first, last);
325 }
326 else
327 {
328 return writeProperties<Policy, U, Ts...>(
329 writeProperty<T>(src, first, last), first + detail::numProperties<T>(), last);
330 }
331 }
333
334 template<typename... Ts>
335 const std::uint8_t *writeElement(
336 const std::uint8_t *src,
337 PlyPropertyConstIterator first,
338 PlyPropertyConstIterator last) const
339 {
340 src = this->writeProperties<FormatWriterPolicy, Ts...>(src, first, last);
341
342 // In case the element defines more properties than the source data,
343 // append the missing properties with a default value of zero.
344 if (detail::numProperties<Ts...>() < static_cast<std::size_t>(std::distance(first, last)))
345 {
346 this->writeMissingProperties(first + detail::numProperties<Ts...>(), last);
347 }
348
349 this->writeNewline();
350
351 return src;
352 }
353};
354
355}
356
357#endif