Tatooine
write.h
Go to the documentation of this file.
1#ifndef TATOOINE_DETAIL_LINE_OPERATIONS_H
2#define TATOOINE_DETAIL_LINE_OPERATIONS_H
3//==============================================================================
4#include <tatooine/line.h>
5#include <tatooine/nan.h>
6#include <tatooine/pow.h>
7//==============================================================================
9//==============================================================================
10template <typename T, typename Writer, typename Names>
11auto write_properties_to_vtk(Writer& writer, Names const& names,
12 range_of_lines auto const& lines) -> void {
13 std::vector<T> prop_collector;
14 for (auto const& [name_to_search, type_to_search] : names) {
15 prop_collector.clear();
16 for (auto const& l : lines) {
17 if (l.has_vertex_property(name_to_search)) {
18 try {
19 auto const& prop = l.template vertex_property<T>(name_to_search);
20 std::ranges::copy(prop.internal_container(),
21 std::back_inserter(prop_collector));
22 } catch (...) {
23 for (std::size_t i = 0; i < l.vertices().size(); ++i) {
24 prop_collector.push_back(nan<T>());
25 }
26 }
27 }
28 }
29 writer.write_scalars(name_to_search, prop_collector);
30 }
31}
32//==============================================================================
33} // namespace tatooine::detail::line
34//==============================================================================
35namespace tatooine {
36//==============================================================================
37template <range_of_lines Lines>
38auto write_vtp(Lines const& lines, filesystem::path const& path) -> void {
39 auto file = std::ofstream{path, std::ios::binary};
40 if (!file.is_open()) {
41 throw std::runtime_error{"Could not write " + path.string()};
42 }
43 auto offset = std::size_t{};
44 using header_type = std::uint32_t;
45 using connectivity_int_t = std::int32_t;
46 using offset_int_t = connectivity_int_t;
47 using real_type = typename Lines::value_type::real_type;
48 file << "<VTKFile"
49 << " type=\"PolyData\""
50 << " version=\"1.0\" "
51 "byte_order=\"LittleEndian\""
52 << " header_type=\""
53 << vtk::xml::to_data_type<header_type>()
54 << "\">\n";
55 file << " <PolyData>\n";
56 for (auto const& l : lines) {
57 file << " <Piece"
58 << " NumberOfPoints=\"" << l.vertices().size() << "\""
59 << " NumberOfPolys=\"0\""
60 << " NumberOfVerts=\"0\""
61 << " NumberOfLines=\""
62 << (l.vertices().size() - (l.is_closed() ? 0 : 1)) << "\""
63 << " NumberOfStrips=\"0\""
64 << ">\n";
65
66 // Points
67 file << " <Points>\n";
68 file << " <DataArray"
69 << " format=\"appended\""
70 << " offset=\"" << offset << "\""
71 << " type=\""
73 vtk::xml::to_data_type<real_type>())
74 << "\" NumberOfComponents=\"3\"/>\n";
75 auto const num_bytes_points =
76 header_type(sizeof(real_type) * 3 * l.vertices().size());
77 offset += num_bytes_points + sizeof(header_type);
78 file << " </Points>\n";
79
80 // Lines
81 file << " <Lines>\n";
82 // Lines - connectivity
83 file << " <DataArray format=\"appended\" offset=\"" << offset
84 << "\" type=\""
86 vtk::xml::to_data_type<connectivity_int_t>())
87 << "\" Name=\"connectivity\"/>\n";
88 auto const num_bytes_connectivity =
89 (l.vertices().size() - (l.is_closed() ? 0 : 1)) * 2 *
90 sizeof(connectivity_int_t);
91 offset += num_bytes_connectivity + sizeof(header_type);
92 // Lines - offsets
93 file << " <DataArray format=\"appended\" offset=\"" << offset
94 << "\" type=\""
96 vtk::xml::to_data_type<offset_int_t>())
97 << "\" Name=\"offsets\"/>\n";
98 auto const num_bytes_offsets =
99 sizeof(offset_int_t) * (l.vertices().size() - (l.is_closed() ? 0 : 1));
100 offset += num_bytes_offsets + sizeof(header_type);
101 file << " </Lines>\n";
102 file << " </Piece>\n";
103 }
104 file << " </PolyData>\n";
105 file << " <AppendedData encoding=\"raw\">\n _";
106 // Writing vertex data to appended data section
107 for (auto const& l : lines) {
108 auto arr_size = header_type{};
109 arr_size = header_type(sizeof(real_type) * 3 * l.vertices().size());
110 file.write(reinterpret_cast<char const*>(&arr_size), sizeof(header_type));
111 auto zero = real_type(0);
112 for (auto const v : l.vertices()) {
113 if constexpr (Lines::value_type::num_dimensions() == 2) {
114 file.write(reinterpret_cast<char const*>(l.at(v).data()),
115 sizeof(real_type) * 2);
116 file.write(reinterpret_cast<char const*>(&zero), sizeof(real_type));
117 } else if constexpr (Lines::value_type::num_dimensions() == 3) {
118 file.write(reinterpret_cast<char const*>(l.at(v).data()),
119 sizeof(real_type) * 3);
120 }
121 }
122
123 // Writing lines connectivity data to appended data section
124 {
125 auto connectivity_data = std::vector<connectivity_int_t>{};
126 connectivity_data.reserve(
127 (l.vertices().size() - (l.is_closed() ? 0 : 1)) * 2);
128 for (std::size_t i = 0; i < l.vertices().size() - 1; ++i) {
129 connectivity_data.push_back(static_cast<connectivity_int_t>(i));
130 connectivity_data.push_back(static_cast<connectivity_int_t>(i + 1));
131 }
132 if (l.is_closed()) {
133 connectivity_data.push_back(
134 static_cast<connectivity_int_t>(l.vertices().size()) - 1);
135 connectivity_data.push_back(0);
136 }
137 arr_size = static_cast<header_type>(connectivity_data.size() *
138 sizeof(connectivity_int_t));
139 file.write(reinterpret_cast<char const*>(&arr_size), sizeof(header_type));
140 file.write(reinterpret_cast<char const*>(connectivity_data.data()),
141 arr_size);
142 }
143
144 // Writing lines offsets to appended data section
145 {
146 auto offsets = std::vector<offset_int_t>(
147 l.vertices().size() - (l.is_closed() ? 0 : 1), 2);
148 for (std::size_t i = 1; i < size(offsets); ++i) {
149 offsets[i] += offsets[i - 1];
150 }
151 arr_size = static_cast<header_type>(sizeof(offset_int_t)) *
152 static_cast<header_type>(l.vertices().size() -
153 static_cast<header_type>((l.is_closed() ? 0 : 1)));
154 file.write(reinterpret_cast<char const*>(&arr_size), sizeof(header_type));
155 file.write(reinterpret_cast<char const*>(offsets.data()), arr_size);
156 }
157 }
158 file << "\n </AppendedData>\n";
159 file << "</VTKFile>";
160}
161// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
162template <range_of_lines Lines>
163auto write_vtk(Lines const& lines, filesystem::path const& path,
164 std::string const& title = "tatooine lines") -> void {
166 if (writer.is_open()) {
167 auto num_pts = std::size_t{};
168 for (const auto& l : lines) {
169 num_pts += l.vertices().size();
170 }
171 auto points =
172 std::vector<std::array<typename Lines::value_type::real_type, 3>>{};
173 auto line_seqs = std::vector<std::vector<std::size_t>>{};
174 points.reserve(num_pts);
175 line_seqs.reserve(lines.size());
176
177 auto cur_first = std::size_t{};
178 for (const auto& l : lines) {
179 // add points
180 for (const auto& v : l.vertices()) {
181 auto const& p = l[v];
182 if constexpr (Lines::value_type::num_dimensions() == 3) {
183 points.push_back({p(0), p(1), p(2)});
184 } else {
185 points.push_back({p(0), p(1), 0});
186 }
187 }
188
189 // add lines
190 boost::iota(line_seqs.emplace_back(l.vertices().size()), cur_first);
191 if (l.is_closed()) {
192 line_seqs.back().push_back(cur_first);
193 }
194 cur_first += l.vertices().size();
195 }
196
197 // write
198 writer.set_title(title);
199 writer.write_header();
200 writer.write_points(points);
201 writer.write_lines(line_seqs);
202 writer.write_point_data(num_pts);
203
204 auto names = std::set<std::pair<std::string, std::type_info const*>>{};
205 // collect names
206 for (const auto& l : lines) {
207 for (auto const& [name, prop] : l.vertex_properties()) {
208 names.insert(std::pair{name, &prop->type()});
209 }
210 }
211 detail::line::write_properties_to_vtk<double>(writer, names,
212 lines);
213 writer.close();
214 }
215}
216//------------------------------------------------------------------------------
217template <range_of_lines Lines>
218auto write(Lines const& lines, filesystem::path const& path) -> void {
219 using line_t = std::ranges::range_value_t<Lines>;
220 auto const ext = path.extension();
221 if constexpr (line_t::num_dimensions() == 2 ||
222 line_t::num_dimensions() == 3) {
223 if (ext == ".vtk") {
224 write_vtk(lines, path);
225 return;
226 } else if (ext == ".vtp") {
227 write_vtp(lines, path);
228 return;
229 }
230 }
231 throw std::runtime_error("Could not write lines. Unknown file extension: \"" +
232 ext.string() + "\".");
233}
234//------------------------------------------------------------------------------
235template <typename Lines, typename MaxDist>
236auto merge(range_of_lines auto lines, MaxDist max_length) {
237 using line_t = std::ranges::range_value_t<Lines>;
238 auto merged = std::list<line_t> {};
239 merged.emplace_back(std::move(lines.back()));
240 lines.pop_back();
241
242 while (!lines.empty()) {
243 auto min_d = std::numeric_limits<typename line_t::real_type>::max();
244 auto best_it = std::end(lines);
245 auto merged_take_front = false;
246 auto it_take_front = false;
247 for (auto it = std::begin(lines); it != std::end(lines); ++it) {
248 if (const auto d =
249 distance(merged.back().front_vertex(), it->front_vertex());
250 d < min_d && d < max_length) {
251 min_d = d;
252 best_it = it;
253 merged_take_front = true;
254 it_take_front = true;
255 }
256 if (const auto d =
257 distance(merged.back().back_vertex(), it->front_vertex());
258 d < min_d && d < max_length) {
259 min_d = d;
260 best_it = it;
261 merged_take_front = false;
262 it_take_front = true;
263 }
264 if (const auto d =
265 distance(merged.back().front_vertex(), it->back_vertex());
266 d < min_d && d < max_length) {
267 min_d = d;
268 best_it = it;
269 merged_take_front = true;
270 it_take_front = false;
271 }
272 if (const auto d =
273 distance(merged.back().back_vertex(), it->back_vertex());
274 d < min_d && d < max_length) {
275 min_d = d;
276 best_it = it;
277 merged_take_front = false;
278 it_take_front = false;
279 }
280 }
281
282 if (best_it != end(lines)) {
283 if (merged_take_front) {
284 if (it_take_front) {
285 for (const auto& v : best_it->vertices()) {
286 merged.back().push_front(v);
287 }
288 } else {
289 for (const auto& v :
290 best_it->vertices() | boost::adaptors::reversed) {
291 merged.back().push_front(v);
292 }
293 }
294 } else {
295 if (it_take_front) {
296 for (const auto& v : best_it->vertices()) {
297 merged.back().push_back(v);
298 }
299 } else {
300 for (const auto& v :
301 best_it->vertices() | boost::adaptors::reversed) {
302 merged.back().push_back(v);
303 }
304 }
305 }
306 lines.erase(best_it);
307 } else {
308 merged.emplace_back(std::move(lines.back()));
309 lines.pop_back();
310 }
311 }
312
313 return merged;
314}
315//------------------------------------------------------------------------------
316auto filter_length(range_of_lines auto const& lines,
317 arithmetic auto const max_length) {
318 auto filtered =
319 std::vector<typename std::decay_t<decltype(lines)>::value_type>{};
320 for (auto const& l : lines) {
321 if (l.arc_length() > max_length) {
322 filtered.push_back(l);
323 }
324 }
325 return filtered;
326}
327//------------------------------------------------------------------------------
328template <typename Real>
329auto intersections(std::vector<line<Real, 2>> const& lines0,
330 std::vector<line<Real, 2>> const& lines1) {
331 static auto const eps = Real(1e-6);
332 auto xs = std::vector<vec<Real, 2>>{};
333 for (auto const& l0 : lines0) {
334 for (auto const& l1 : lines1) {
335 for (std::size_t i = 0; i < l0.vertices().size() - 1; ++i) {
336 for (std::size_t j = 0; j < l1.vertices().size() - 1; ++j) {
337 auto const& p0 = l0.vertex_at(i);
338 auto const& p1 = l0.vertex_at(i + 1);
339 auto const& p2 = l1.vertex_at(j);
340 auto const& p3 = l1.vertex_at(j + 1);
341 auto const d01 = p0 - p1;
342 auto const d23 = p2 - p3;
343 auto const d02 = p0 - p2;
344
345 auto const denom = d01.x() * d23.y() - d01.y() * d23.x();
346 if (std::abs(denom) < eps) {
347 continue;
348 }
349 auto const inv_denom = 1 / denom;
350
351 auto const nom_t = d02.x() * d23.y() - d02.y() * d23.x();
352 auto const t = nom_t * inv_denom;
353 if (0 > t || t > 1) {
354 continue;
355 }
356 auto const nom_u = -(d01.x() * d02.y() - d01.y() * d02.x());
357 auto const u = nom_u * inv_denom;
358 if (0 > u || u > 1) {
359 continue;
360 }
361 xs.push_back(p2 - u * d23);
362 }
363 }
364 }
365 }
366 return xs;
367}
368//==============================================================================
369} // namespace tatooine
370//==============================================================================
371#endif
Definition: vtk_legacy.h:448
Definition: concepts.h:33
Definition: line.h:844
Definition: merge.h:8
auto write_properties_to_vtk(Writer &writer, Names const &names, range_of_lines auto const &lines) -> void
Definition: write.h:11
auto to_string(data_type const t) -> std::string_view
Definition: algorithm.h:6
constexpr auto distance(Iter const &it0, Iter const &it1)
Definition: iterator_facade.h:372
auto write_vtk(Lines const &lines, filesystem::path const &path, std::string const &title="tatooine lines") -> void
Definition: write.h:163
typename value_type_impl< T >::type value_type
Definition: type_traits.h:280
auto end(Range &&range)
Definition: iterator_facade.h:322
auto write(Lines const &lines, filesystem::path const &path) -> void
Definition: write.h:218
auto intersections(std::vector< line< Real, 2 > > const &lines0, std::vector< line< Real, 2 > > const &lines1)
Definition: write.h:329
auto write_vtp(Lines const &lines, filesystem::path const &path) -> void
Definition: write.h:38
auto filter_length(range_of_lines auto const &lines, arithmetic auto const max_length)
Definition: write.h:316
auto merge(Lines const &unmerged_lines, Eps const eps=1e-13)
Merges a set of lines and combines lines with equal vertex endings.
Definition: merge.h:165
auto size(vec< ValueType, N > const &v)
Definition: vec.h:148
Definition: line.h:35