Tatooine
merge.h
Go to the documentation of this file.
1#ifndef TATOOINE_GEOMETRY_DETAIL_LINE_MERGE_H
2#define TATOOINE_GEOMETRY_DETAIL_LINE_MERGE_H
3//==============================================================================
4#include <tatooine/line.h>
5
6#include <vector>
7//==============================================================================
9//==============================================================================
10enum class line_connection : std::uint8_t {
11 no_connection = 0,
16};
17//==============================================================================
19template <typename Real, std::size_t N>
21 tatooine::line<Real, N> const &line1,
22 floating_point auto const eps) {
23 auto connection = line_connection::no_connection;
24 if (approx_equal(line0.back_vertex(), line1.back_vertex(), eps)) {
25 connection =
26 static_cast<std::uint8_t>(connection) |
27 static_cast<std::uint8_t>(line_connection::front_common_front_common);
28 }
29 if (approx_equal(line0.front_vertex(), line1.front_vertex(), eps)) {
30 connection =
31 static_cast<std::uint8_t>(connection) |
32 static_cast<std::uint8_t>(line_connection::common_back_common_back);
33 }
34 if (approx_equal(line0.front_vertex(), line1.back_vertex(), eps)) {
35 connection =
36 static_cast<std::uint8_t>(connection) |
37 static_cast<std::uint8_t>(line_connection::common_back_front_common);
38 }
39 if (approx_equal(line0.back_vertex(), line1.front_vertex(), eps)) {
40 connection =
41 static_cast<std::uint8_t>(connection) |
42 static_cast<std::uint8_t>(line_connection::front_common_common_back);
43 }
44 return connection;
45}
46//------------------------------------------------------------------------------
48template <typename Real, std::size_t N>
50 tatooine::line<Real, N> const &line1,
51 floating_point auto const eps) {
52 if (line0.empty() || line1.empty()) {
54 }
55 if (approx_equal(line0.back_vertex(), line1.front_vertex(), eps)) {
57 }
58 if (approx_equal(line0.front_vertex(), line1.front_vertex(), eps)) {
60 }
61 if (approx_equal(line0.front_vertex(), line1.back_vertex(), eps)) {
63 }
64 if (approx_equal(line0.back_vertex(), line1.back_vertex(), eps)) {
66 }
68}
69//------------------------------------------------------------------------------
70template <typename Real, std::size_t N>
73 line_connection const conn) -> bool {
74 using std::ranges::copy;
75 using std::ranges::subrange;
76 using std::views::reverse;
77
78 auto &data0 = line0.vertices().data_container();
79 auto data0_rev = data0 | reverse;
80
81 switch (conn) {
83 auto line0_without_common_vertex =
84 subrange{next(begin(data0_rev)), end(data0_rev)};
85 copy(line0_without_common_vertex, std::front_inserter(line1));
86 line0.clear();
87 return true;
88 }
90 auto line0_without_common_vertex =
91 subrange{next(begin(data0)), end(data0)};
92 copy(line0_without_common_vertex, std::front_inserter(line1));
93 line0.clear();
94 return true;
95 }
97 auto line0_without_common_vertex =
98 subrange{next(begin(data0)), end(data0)};
99 copy(line0_without_common_vertex, std::back_inserter(line1));
100 line0.clear();
101 return true;
102 }
104 auto line0_without_common_vertex =
105 subrange{next(begin(data0_rev)), end(data0_rev)};
106 copy(line0_without_common_vertex, std::back_inserter(line1));
107 line0.clear();
108 return true;
109 }
111 default:
112 return false;
113 }
114}
115//------------------------------------------------------------------------------
116template <floating_point Eps = real_number>
117auto connect_lines_if_possible(std::forward_iterator auto line0,
118 std::forward_iterator auto line1,
119 line_connection const conn) -> bool {
120 using std::ranges::copy;
121 using std::ranges::subrange;
122 using std::views::reverse;
123 if (line0 == line1 || line0->empty() || line1->empty()) {
124 return false;
125 }
126
127 return connect_lines_if_possible(*line0, *line1, conn);
128}
129//------------------------------------------------------------------------------
130auto connect_lines_if_possible(std::forward_iterator auto line0,
131 std::forward_iterator auto line1,
132 floating_point auto const eps) {
134 line0, line1, determine_any_line_connection(*line0, *line1, eps));
135}
136//------------------------------------------------------------------------------
137template <typename Real, std::size_t N>
140 floating_point auto const eps) {
142 line0, line1, determine_any_line_connection(line0, line1, eps));
143}
144//------------------------------------------------------------------------------
147template <range_of_lines Lines, floating_point Eps = real_number>
148auto connect_lines_if_possible(Lines &lines, Eps const eps) -> void {
149 for (auto line0 = begin(lines); line0 != end(lines); ++line0) {
150 for (auto line1 = begin(lines); line1 != end(lines); ++line1) {
151 connect_lines_if_possible(line0, line1, eps);
152 }
153 }
154 auto const is_empty = [](auto const &l) { return l.empty(); };
155 auto const [empty_begin, empty_end] = std::ranges::remove_if(lines, is_empty);
156 lines.erase(empty_begin, empty_end);
157}
158//==============================================================================
159} // namespace tatooine::detail::line
160//==============================================================================
161namespace tatooine {
162//==============================================================================
164template <range_of_lines Lines, floating_point Eps = real_number>
165auto merge(Lines const &unmerged_lines, Eps const eps = 1e-13) {
166 using line_t = std::ranges::range_value_t<Lines>;
167 auto merged_lines = std::vector<std::vector<line_t>>(unmerged_lines.size());
168
169 auto unmerged_it = begin(unmerged_lines);
170 for (auto &merged_line : merged_lines) {
171 merged_line.push_back({*unmerged_it});
172 ++unmerged_it;
173 }
174
175 auto num_merge_steps =
176 static_cast<std::size_t>(ceil(log2(unmerged_lines.size())));
177
178 for (std::size_t i = 0; i < num_merge_steps; i++) {
179 std::size_t offset = tatooine::pow(std::size_t(2), i);
180
181#pragma omp parallel for
182 for (std::size_t j = 0; j < unmerged_lines.size(); j += offset * 2) {
183 auto left = j;
184 auto right = j + offset;
185 if (right < unmerged_lines.size()) {
186 // move lines from right to left
187 merged_lines[left].reserve(size(merged_lines[left]) +
188 size(merged_lines[right]));
189 std::ranges::move(merged_lines[right],
190 std::back_inserter(merged_lines[left]));
191 merged_lines[right].clear();
192 merged_lines[right].shrink_to_fit();
193 // merge lines
194 detail::line::connect_lines_if_possible(merged_lines[left], eps);
195 }
196 }
197 }
198 return merged_lines.front();
199}
200//------------------------------------------------------------------------------
202template <range_of_lines Lines, floating_point Eps = real_number,
203 execution_policy_tag ExecutionPolicy>
204auto merge(Lines &lines, std::ranges::range_value_t<Lines> line_to_merge,
205 ExecutionPolicy const tag, Eps const eps = 1e-13) -> auto & {
206 using namespace detail::line;
207 auto inserted = std::atomic_bool{false};
208 auto mutex = std::mutex{};
209 auto connections =
210 std::vector<std::pair<std::ranges::iterator_t<Lines>, line_connection>>{};
211 for_loop(
212 [&](auto line_it) {
213 auto connection =
214 determine_any_line_connection(line_to_merge, *line_it, eps);
215 if (connection != line_connection::no_connection) {
216 if constexpr (is_same<ExecutionPolicy,
218 auto lock = std::lock_guard{mutex};
219 connections.emplace_back(line_it, connection);
220 } else {
221 connections.emplace_back(line_it, connection);
222 }
223 }
224 },
225 lines, tag);
226 if (connections.empty()) {
227 lines.push_back(line_to_merge);
228 return lines;
229 }
230 if (connections.size() == 1) {
231 auto const [line_it, connection] = connections.front();
232 connect_lines_if_possible(line_to_merge, *line_it, connection);
233 return lines;
234 }
235 if (connections.size() == 2) {
236 auto const [line0_it, connection0] = connections[0];
237 auto const [line1_it, connection1] = connections[1];
238 connect_lines_if_possible(line_to_merge, *line0_it, connection0);
239 if (connect_lines_if_possible(*line1_it, *line0_it, eps)) {
240 lines.erase(line1_it);
241 }
242 return lines;
243 }
244
245 return lines;
246}
247//==============================================================================
248} // namespace tatooine
249//==============================================================================
250#endif
Definition: concepts.h:30
Definition: merge.h:8
auto determine_line_connection_cases(tatooine::line< Real, N > const &line0, tatooine::line< Real, N > const &line1, floating_point auto const eps)
Determines all line connection cases.
Definition: merge.h:20
auto determine_any_line_connection(tatooine::line< Real, N > const &line0, tatooine::line< Real, N > const &line1, floating_point auto const eps)
Determines all line connection cases.
Definition: merge.h:49
auto end(vertex_container< Real, NumDimensions, Handle > const &it)
Definition: vertex_container.h:46
line_connection
Definition: merge.h:10
auto begin(vertex_container< Real, NumDimensions, Handle > const &it)
Definition: vertex_container.h:41
auto next(const vertex_iterator< Real, NumDimensions, Handle > &it, size_t inc=1)
Definition: vertex_iterator.h:67
auto connect_lines_if_possible(tatooine::line< Real, N > &line0, tatooine::line< Real, N > &line1, line_connection const conn) -> bool
Definition: merge.h:71
Definition: algorithm.h:6
auto begin(Range &&range)
Definition: iterator_facade.h:318
constexpr auto pow(arithmetic auto const x)
Definition: math.h:30
constexpr auto approx_equal(T0 const &lhs, T1 const &rhs, common_type< tatooine::value_type< T0 >, tatooine::value_type< T1 > > eps=1e-6)
for comparison
Definition: tensor_utility.h:11
static constexpr auto is_same
Definition: type_traits.h:42
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
constexpr auto log2(arithmetic auto const x)
Definition: math.h:24
constexpr auto for_loop(Iteration &&iteration, execution_policy::sequential_t, Ranges(&&... ranges)[2]) -> void
Use this function for creating a sequential nested loop.
Definition: for_loop.h:336
Definition: line.h:35
auto front_vertex() const -> auto const &
Definition: line.h:170
auto empty() const
Definition: line.h:140
auto back_vertex() const -> auto const &
Definition: line.h:173