FuncViz lib
Generalized bitree/quadtree/octree library
Loading...
Searching...
No Matches
MR_rt_to_cc.hpp
Go to the documentation of this file.
1// -*- Mode:C++; Coding:us-ascii-unix; fill-column:158 -*-
2/*******************************************************************************************************************************************************.H.S.**/
3/**
4 @file MR_rt_to_cc.hpp
5 @author Mitch Richling http://www.mitchr.me/
6 @date 2024-07-13
7 @brief Implimentation for the MR_rt_to_cc class.@EOL
8 @keywords tree cell complex
9 @std C++23
10 @see MR_rect_tree.hpp, MR_cell_cplx.hpp
11 @copyright
12 @parblock
13 Copyright (c) 2024, Mitchell Jay Richling <http://www.mitchr.me/> All rights reserved.
14
15 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
16
17 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
18
19 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation
20 and/or other materials provided with the distribution.
21
22 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software
23 without specific prior written permission.
24
25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30 DAMAGE.
31 @endparblock
32*/
33/*******************************************************************************************************************************************************.H.E.**/
34
35////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
36#ifndef MJR_INCLUDE_MR_rt_to_cc
37
38////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
39#include <tuple> /* STL tuples C++11 */
40#include <vector> /* STL vector C++11 */
41#include <string> /* C++ strings C++11 */
42#include <variant> /* C++ variant type C++17 */
43
44////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
45// Put everything in the mjr namespace
46namespace mjr {
47 /** @brief Tessellates a MR_rect_tree object, and places the result into an MR_cell_cplx object.
48
49 From a structural perspective this class simply a templated collection of types and static methods all designed to work with pairs of MR_rect_tree and
50 MR_cell_cplx objects. From a functional, or pattern, perspective this class might classified as a MR_cell_cplx pseudo-constructor or proto-factory. I
51 think of it as a bridge between MR_rect_tree and MR_cell_cplx objects. A collection of helper types that ease working with pairs of MR_rect_tree and
52 MR_cell_cplx objects containing data derived from the same sampled function.
53
54 @tparam rt_t The type of supported MR_rect_tree objects
55 @tparam cc_t The type of supported MR_cell_cplx objects */
56 template <class rt_t, class cc_t>
57 requires (std::is_same<typename rt_t::src_t, typename cc_t::uft_t>::value)
59
60 public:
61
62 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
63 /** @name Types imported from MR_rect_tree and MR_cell_cplx. */
64 //@{
65 //--------------------------------------------------------------------------------------------------------------------------------------------------------
66 typedef typename cc_t::node_data_idx_lst_t cc_node_data_idx_lst_t;
67 typedef typename cc_t::node_data_t cc_node_data_t;
68 typedef typename cc_t::node_idx_list_t cc_node_idx_list_t;
69 typedef typename cc_t::node_idx_t cc_node_idx_t;
70 typedef typename cc_t::cell_verts_t cc_cell_verts_t;
71 typedef typename cc_t::uft_t cc_uft_t;
72 typedef typename rt_t::diti_list_t rt_diti_list_t;
73 typedef typename rt_t::diti_t rt_diti_t;
74 typedef typename rt_t::drpt_t rt_drpt_t;
75 typedef typename rt_t::drpt2real_func_t rt_drpt2real_func_t;
76 typedef typename rt_t::rrpt_t rt_rrpt_t;
77 typedef typename rt_t::drpt2rrpt_func_t rt_drpt2rrpt_func_t;
78 //@}
79
80 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
81 /** @name Describe point source. */
82 //@{
83 //--------------------------------------------------------------------------------------------------------------------------------------------------------
84 /** Specify a source space for a data index. */
85 enum class val_src_spc_t { FDOMAIN, //!< Sample function domain space.
86 FRANGE, //!< Sample function range space.
87 CONSTANT //!< A pseudo-source that returns a constant.
88 };
89 //--------------------------------------------------------------------------------------------------------------------------------------------------------
90 /** Type to hold an integer or float. */
91 typedef std::variant<int, cc_uft_t> iorf_t;
92 //--------------------------------------------------------------------------------------------------------------------------------------------------------
93 /** Type used to hold a description of how to extract a scalar value from a tree object */
94 typedef std::tuple<val_src_spc_t, iorf_t> val_src_t;
95 //--------------------------------------------------------------------------------------------------------------------------------------------------------
96 /** A list of val_src_t objects. */
97 typedef std::vector<val_src_t> val_src_lst_t;
98 //@}
99
100 private:
101 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
102 /** @name Utility Functions. */
103 //@{
104 //--------------------------------------------------------------------------------------------------------------------------------------------------------
105 /** Create approprate inputs for ccplx.create_dataset_to_point_mapping() from tree data.
106 @param rtree The MR_rect_tree with source data
107 @param ccplx The MR_cell_cplx to populate wiht a pont mapping
108 @param rt_dil Description of data sources */
109 inline static void create_dataset_to_point_mapping(const rt_t& rtree, cc_t& ccplx, const val_src_lst_t& rt_dil) {
110 cc_node_data_idx_lst_t cc_data_idx_lst(3);
111 for(int i=0; i<3; ++i)
112 if(get<0>(rt_dil[i]) == val_src_spc_t::FDOMAIN)
113 cc_data_idx_lst[i] = get<int>(get<1>(rt_dil[i]));
114 else if(get<0>(rt_dil[i]) == val_src_spc_t::FRANGE)
115 cc_data_idx_lst[i] = get<int>(get<1>(rt_dil[i])) + rtree.domain_dimension;
116 else if(get<0>(rt_dil[i]) == val_src_spc_t::CONSTANT)
117 cc_data_idx_lst[i] = get<cc_uft_t>(get<1>(rt_dil[i]));
118 ccplx.create_dataset_to_point_mapping(cc_data_idx_lst);
119 }
120 //--------------------------------------------------------------------------------------------------------------------------------------------------------
121 /** Given rt coordinates, extract point/scalar/vector data, and add point/data to cc
122 @param ccplx The MR_cell_cplx to populate with geometry
123 @param rtree The MR_rect_tree with source data
124 @param diti The point coordinate in rtree */
125 inline static cc_node_idx_t add_node(cc_t& ccplx, const rt_t& rtree, rt_diti_t diti) {
126 return add_node(ccplx, rtree.diti_to_drpt(diti), rtree.get_sample(diti));
127 }
128 //--------------------------------------------------------------------------------------------------------------------------------------------------------
129 /** Given rt coordinates, extract point/scalar/vector data, and add point/data to cc
130 @param ccplx The MR_cell_cplx to populate with geometry
131 @param dom_pnt Domain point
132 @param rng_pnt Range point */
133 inline static cc_node_idx_t add_node(cc_t& ccplx, rt_drpt_t dom_pnt, rt_rrpt_t rng_pnt) {
134 return ccplx.add_node(rt_pnt_to_cc_pnt(dom_pnt, rng_pnt));
135 }
136 //--------------------------------------------------------------------------------------------------------------------------------------------------------
137 /** Given rt_t domain & range points, produce a cc_t point data vector.
138 @param dom_pnt Domain point
139 @param rng_pnt Range point */
140 inline static cc_node_data_t rt_pnt_to_cc_pnt(rt_drpt_t dom_pnt, rt_rrpt_t rng_pnt) {
142 if constexpr (rt_t::domain_dimension == 1)
143 pd.push_back(dom_pnt);
144 else
145 for(auto v: dom_pnt)
146 pd.push_back(v);
147 if constexpr (rt_t::range_dimension == 1)
148 pd.push_back(rng_pnt);
149 else
150 for(auto v: rng_pnt)
151 pd.push_back(v);
152 return pd;
153 }
154 //--------------------------------------------------------------------------------------------------------------------------------------------------------
155 /** Transform a MR_cell_cplx::node_data_t value into MR_rect_tree::drpt_t.
156 @param pd The point data to convert.*/
157 inline static rt_drpt_t node_data_to_drpt(const cc_node_data_t& pd) {
158 rt_drpt_t ret;
159 if constexpr (rt_t::domain_dimension == 1) {
160 ret = pd[0];
161 } else {
162 for(int i=0; i<rt_t::domain_dimension; ++i)
163 ret[i] = pd[i];
164 }
165 return ret;
166 }
167 //@}
168
169 public:
170
171 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
172 /** @name Utility Functions. */
173 //@{
174 //--------------------------------------------------------------------------------------------------------------------------------------------------------
175 //--------------------------------------------------------------------------------------------------------------------------------------------------------
176 /** Convert a MR_rect_tree range index into an index for a point data array
177 @param tree_range_index value to convert */
178 inline static int rt_rng_idx_to_pd_idx(int tree_range_index) {
179 return (tree_range_index + rt_t::domain_dimension);
180 }
181 //--------------------------------------------------------------------------------------------------------------------------------------------------------
182 /** Given an edge with one good point and one NaN point, find the longest segment from the good point toward the NaN point.
183
184 Note we normally use this function when we detect a NaN in a geometric point (i.e. the things with a node_idx_t). This solver solves until
185 the return of func has no NaNs. Those two criteria might not be the same thing, but it's OK.
186
187 @param ccplx The MR_cell_cplx to populate with geometry
188 @param rtree The MR_rect_tree with source data
189 @param good_point_ccplx_index Good point index in the ccplx object
190 @param good_point_rtree_index Good point index in the rtree object
191 @param sick_point_rtree_index Bad point index in the rtree object
192 @param func The function to use for the solver
193 @param solver_epsilon Used as a distance threshold between sick point and solved endpoint in the tree domain space. */
194 static cc_node_idx_t nan_edge_solver(cc_t& ccplx,
195 const rt_t& rtree,
196 cc_node_idx_t good_point_ccplx_index,
197 rt_diti_t good_point_rtree_index,
198 rt_diti_t sick_point_rtree_index,
200 cc_uft_t solver_epsilon=cc_t::epsilon/100
201 ) {
202 // Solver cache. Clear it if we have a different rtree object from last time.
203 static std::unordered_map<rt_diti_t, std::unordered_map<rt_diti_t, cc_node_idx_t>> nan_solver_cache;
204 static const rt_t* rtree_cache = nullptr;
205 if (rtree_cache != &rtree) {
206 nan_solver_cache.clear();
207 rtree_cache = &rtree;
208 }
209 // Check to see if we solved this one before
210 if (nan_solver_cache.contains(sick_point_rtree_index))
211 if (nan_solver_cache[sick_point_rtree_index].contains(good_point_rtree_index))
212 return nan_solver_cache[sick_point_rtree_index][good_point_rtree_index];
213 // Apparently we need to solve this one as it's not in the case
214 rt_drpt_t good_point_drpt = rtree.diti_to_drpt(good_point_rtree_index);
215 rt_drpt_t sick_point_drpt = rtree.diti_to_drpt(sick_point_rtree_index);
216 rt_rrpt_t good_point_rrpt = rtree.get_sample(good_point_rtree_index);
217 rt_drpt_t init_point_drpt = good_point_drpt;
218 while ( (rtree.drpt_distance_inf(good_point_drpt, sick_point_drpt) > solver_epsilon) ) {
219 rt_drpt_t md_point_drpt = rtree.drpt_midpoint(good_point_drpt, sick_point_drpt);
220 rt_rrpt_t y = func(md_point_drpt);
221 if (rtree.rrpt_is_nan(y)) {
222 sick_point_drpt = md_point_drpt;
223 } else {
224 good_point_drpt = md_point_drpt;
225 good_point_rrpt = y;
226 }
227 }
228 // Figure out what to return, add it to the cache, and return.
229 cc_node_idx_t ret;
230 if (rtree.drpt_distance_inf(good_point_drpt, init_point_drpt) < (ccplx.epsilon)) // Use ccplx here!!!
231 ret = good_point_ccplx_index;
232 else
233 ret = add_node(ccplx, good_point_drpt, good_point_rrpt);
234 nan_solver_cache[sick_point_rtree_index][good_point_rtree_index] = ret;
235 return ret;
236 }
237 //@}
238
239 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
240 /** @name Poly data construction */
241 //@{
242 //--------------------------------------------------------------------------------------------------------------------------------------------------------
243 /** Populate attached MR_cell_cplx object from data in attached MR_rect_tree object.
244
245 construct_geometry_fans(), unlike the other geometry construction methods, is capable of "healing" some broken edges -- edges with one good point
246 and one NaN point. It uses the origional sampling function to solve along the edge to produce the longest non-NaN edge possible. Then it
247 constructs cells using the new piont(s). If func is nullptr, then no edge healing is preformed. This feature works for segments & triangles only.
248
249 @verbatim
250 | Geom | Dom Dim | Out Dim | Result |
251 |------------+---------+---------+--------------------|
252 | FANS | 2 | 1 | Triangle Edges |
253 | FANS | 2 | 2 | Triangles |
254 | FANS | 3 | 1 | Pyramid Edges |
255 | FANS | 3 | 2 | Pyramid Faces |
256 | FANS | 3 | 3 | Solid Pyramids |
257 @endverbatim
258
259 @param ccplx The MR_cell_cplx to populate with geometry
260 @param rtree The MR_rect_tree with source data
261 @param cells List of cells to output from rtree
262 @param output_dimension Parts of cells to output
263 @param point_src Point sources
264 @param func The function was used to sample the tree */
265 static int construct_geometry_fans(cc_t& ccplx,
266 const rt_t& rtree,
267 rt_diti_list_t cells,
268 int output_dimension,
269 val_src_lst_t point_src,
270 rt_drpt2rrpt_func_t func = nullptr
271 ) {
272 create_dataset_to_point_mapping(rtree, ccplx, point_src);
273 if (rtree.domain_dimension == 1) {
274 for(auto& cell: cells) {
275 cc_node_idx_t ctr_pnti = add_node(ccplx, rtree, cell);
276 rt_diti_list_t corners = rtree.ccc_get_corners(cell);
277 cc_node_idx_t cn0_pnti = add_node(ccplx, rtree, corners[0]);
278 cc_node_idx_t cn1_pnti = add_node(ccplx, rtree, corners[1]);
279 if (func) { // We have a func, so we can "heal" broken edges.
280 if (ctr_pnti < 0) { // Center: Broken. Left:
281 if(cn0_pnti >= 0) { // Center: Broken. Left: Good.
282 cc_node_idx_t np = nan_edge_solver(ccplx, rtree, cn0_pnti, corners[0], cell, func);
283 ccplx.add_cell(cc_t::cell_kind_t::SEGMENT, {cn0_pnti, np}, output_dimension);
284 }
285 if(cn1_pnti >= 0) { // Center: Broken. Right: Good.
286 cc_node_idx_t np = nan_edge_solver(ccplx, rtree, cn1_pnti, corners[1], cell, func);
287 ccplx.add_cell(cc_t::cell_kind_t::SEGMENT, {np, cn1_pnti}, output_dimension);
288 }
289 } else { // Center: Good.
290 if(cn0_pnti < 0) { // Center: Good. Left: Broken.
291 cc_node_idx_t np = nan_edge_solver(ccplx, rtree, ctr_pnti, cell, corners[0], func);
292 ccplx.add_cell(cc_t::cell_kind_t::SEGMENT, {np, ctr_pnti}, output_dimension);
293 } else { // Center: Good. Left: Good.
294 ccplx.add_cell(cc_t::cell_kind_t::SEGMENT, {cn0_pnti, ctr_pnti}, output_dimension);
295 }
296 if(cn1_pnti < 0) { // Center: Good. Right: Broken.
297 cc_node_idx_t np = nan_edge_solver(ccplx, rtree, ctr_pnti, cell, corners[1], func);
298 ccplx.add_cell(cc_t::cell_kind_t::SEGMENT, {ctr_pnti, np}, output_dimension);
299 } else { // Center: Good. Left: Good.
300 ccplx.add_cell(cc_t::cell_kind_t::SEGMENT, {ctr_pnti, cn1_pnti}, output_dimension);
301 }
302 }
303 } else {
304 ccplx.add_cell(cc_t::cell_kind_t::SEGMENT, {cn0_pnti, ctr_pnti}, output_dimension);
305 ccplx.add_cell(cc_t::cell_kind_t::SEGMENT, {ctr_pnti, cn1_pnti}, output_dimension);
306 }
307 }
308 } else if (rtree.domain_dimension == 2) {
309 for(auto& cell: cells) {
310 if (func) { // We have a func, so we can "heal" broken edges.
311 for(int i=0; i<2; i++) {
312 for(int j=-1; j<2; j+=2) {
313 std::vector<rt_diti_list_t> triangles;
314 rt_diti_list_t nbrs = rtree.get_existing_neighbor(cell, i, j);
315 if (nbrs.size() > 1) {
316 for(auto n: nbrs) {
317 rt_diti_list_t corners = rtree.ccc_get_corners(n, i, -j);
318 if( ((i == 0) && (j == -1)) || ((i == 1) && (j == 1)) )
319 triangles.push_back({corners[1], corners[0], cell});
320 else
321 triangles.push_back({corners[0], corners[1], cell});
322 }
323 } else {
324 rt_diti_list_t corners = rtree.ccc_get_corners(cell, i, j);
325 if( ((i == 0) && (j == -1)) || ((i == 1) && (j == 1)) )
326 triangles.push_back({corners[1], corners[0], cell});
327 else
328 triangles.push_back({corners[0], corners[1], cell});
329 }
330 for(auto triangle: triangles) {
331 std::array<cc_node_idx_t, 3> tpnts {add_node(ccplx, rtree, triangle[0]),
332 add_node(ccplx, rtree, triangle[1]),
333 add_node(ccplx, rtree, triangle[2])};
334 int num_bad = static_cast<int>(std::count_if(tpnts.begin(), tpnts.end(), [](cc_node_idx_t i) { return i<0; }));
335 if (num_bad == 0) {
336 ccplx.add_cell(cc_t::cell_kind_t::TRIANGLE, {tpnts[0], tpnts[1], tpnts[2]}, output_dimension);
337 } else if ((num_bad == 1) || (num_bad == 2)) {
338 // Rotate points so we only have two cases to think about...
339 std::array<int, 3> p {0, 1, 2};
340 if ( ((tpnts[1] < 0) && (num_bad == 1)) || ((tpnts[1] >= 0) && (num_bad == 2)) )
341 p = {1, 2, 0};
342 else if ( ((tpnts[2] < 0) && (num_bad == 1)) || ((tpnts[2] >= 0) && (num_bad == 2)) )
343 p = {2, 0, 1};
344 // Solve for edge 0-1 & 0-2
345 if (num_bad == 1) {
346 cc_node_idx_t np1 = nan_edge_solver(ccplx, rtree, tpnts[p[1]], triangle[p[1]], triangle[p[0]], func);
347 cc_node_idx_t np2 = nan_edge_solver(ccplx, rtree, tpnts[p[2]], triangle[p[2]], triangle[p[0]], func);
348 ccplx.add_cell(cc_t::cell_kind_t::TRIANGLE, {np1, tpnts[p[1]], tpnts[p[2]]}, output_dimension);
349 ccplx.add_cell(cc_t::cell_kind_t::TRIANGLE, {tpnts[p[2]], np2, np1}, output_dimension);
350 } else {
351 cc_node_idx_t np1 = nan_edge_solver(ccplx, rtree, tpnts[p[0]], triangle[p[0]], triangle[p[1]], func);
352 cc_node_idx_t np2 = nan_edge_solver(ccplx, rtree, tpnts[p[0]], triangle[p[0]], triangle[p[2]], func);
353 ccplx.add_cell(cc_t::cell_kind_t::TRIANGLE, {tpnts[p[0]], np1, np2}, output_dimension);
354 }
355 }
356 }
357 }
358 }
359 } else { // We don't have a func, so we can can't "heal" broken edges. This is much faster. ;)
360 cc_node_idx_t ctr_pnti = add_node(ccplx, rtree, cell);
361 if (ctr_pnti >= 0) { // Center point was good, let's try and make some triangles...
362 for(int i=0; i<2; i++) {
363 for(int j=-1; j<2; j+=2) {
364 rt_diti_list_t nbrs = rtree.get_existing_neighbor(cell, i, j);
365 if (nbrs.size() > 1) {
366 for(auto n: nbrs) {
367 rt_diti_list_t corners = rtree.ccc_get_corners(n, i, -j);
368 cc_node_idx_t cn0_pnti = add_node(ccplx, rtree, corners[0]);
369 cc_node_idx_t cn1_pnti = add_node(ccplx, rtree, corners[1]);
370 if( ((i == 0) && (j == -1)) || ((i == 1) && (j == 1)) )
371 std::swap(cn0_pnti, cn1_pnti);
372 ccplx.add_cell(cc_t::cell_kind_t::TRIANGLE, {cn0_pnti, cn1_pnti, ctr_pnti}, output_dimension);
373 }
374 } else {
375 rt_diti_list_t corners = rtree.ccc_get_corners(cell, i, j);
376 cc_node_idx_t cn0_pnti = add_node(ccplx, rtree, corners[0]);
377 cc_node_idx_t cn1_pnti = add_node(ccplx, rtree, corners[1]);
378 if( ((i == 0) && (j == -1)) || ((i == 1) && (j == 1)) )
379 std::swap(cn0_pnti, cn1_pnti);
380 ccplx.add_cell(cc_t::cell_kind_t::TRIANGLE, {cn0_pnti, cn1_pnti, ctr_pnti}, output_dimension);
381 }
382 }
383 }
384 }
385 }
386 }
387 } else if (rtree.domain_dimension == 3) {
388 for(auto& cell: cells) {
389 cc_node_idx_list_t new_cell(5);
390 new_cell[4] = add_node(ccplx, rtree, cell);
391 std::array<int, 5> p {0, 1, 3, 2, 4};
392 if (new_cell[4] >= 0) { // Center point was good, let's try and make some pyramids...
393 for(int dim=0; dim<3; dim++) {
394 for(int dir=-1; dir<2; dir+=2) {
395 rt_diti_list_t nbrs = rtree.get_existing_neighbor(cell, dim, dir);
396 if (nbrs.size() > 1) {
397 for(auto n: nbrs) {
398 rt_diti_list_t corners = rtree.ccc_get_corners(n, dim, -dir);
399 for(int k=0; k<4; ++k)
400 new_cell[p[k]] = add_node(ccplx, rtree, corners[k]);
401 ccplx.add_cell(cc_t::cell_kind_t::PYRAMID, new_cell, output_dimension);
402 }
403 } else {
404 rt_diti_list_t corners = rtree.ccc_get_corners(cell, dim, dir);
405 for(int k=0; k<4; ++k)
406 new_cell[p[k]] = add_node(ccplx, rtree, corners[k]);
407 ccplx.add_cell(cc_t::cell_kind_t::PYRAMID, new_cell, output_dimension);
408 }
409 }
410 }
411 }
412 }
413 } else { // if (rtree.domain_dimension > 3) {
414 std::cout << "ERROR: construct_geometry_fans: output_dimension>3 not supported for output_dimension>0!" << std::endl;
415 return 1;
416 }
417 return 0;
418 }
419 //--------------------------------------------------------------------------------------------------------------------------------------------------------
420 /** @overload */
421 static int construct_geometry_fans(cc_t& ccplx,
422 const rt_t& rtree,
423 int output_dimension,
424 val_src_lst_t point_src,
425 rt_drpt2rrpt_func_t func = nullptr
426 ) {
427 return construct_geometry_fans(ccplx, rtree, rtree.get_leaf_cells(), output_dimension, point_src, func);
428 }
429 //--------------------------------------------------------------------------------------------------------------------------------------------------------
430 /** Populate attached MR_cell_cplx object from data in attached MR_rect_tree object.
431
432 Only 0D vertex cells are produced. While similar results may be obtained by setting the output_dimension to zero and calling
433 construct_geometry_fan() or construct_geometry_rects(), this method is much faster. This method also provides the option of only outputting centers without
434 corners.
435
436 @verbatim
437 | Geom | output_centers | output_corners | Result | output_dimension=0 equivalent |
438 |--------+----------------+----------------+---------------+-------------------------------|
439 | POINTS | true | true | cell vertexes | construct_geometry_fans |
440 | POINTS | true | false | cell centers | |
441 | POINTS | false | true | cell corners | construct_geometry_rects |
442 | POINTS | false | false | No Points | |
443 @endverbatim
444
445 @param ccplx The MR_cell_cplx to populate with geometry
446 @param rtree The MR_rect_tree with source data
447 @param cells List of tree cells from which to construct geometry
448 @param point_src Point sources
449 @param output_centers Create vertexes for cell centers
450 @param output_corners Create vertexes for cell corners*/
451 static int construct_geometry_points(cc_t& ccplx,
452 const rt_t& rtree,
453 rt_diti_list_t cells,
454 val_src_lst_t point_src,
455 bool output_centers,
456 bool output_corners
457 ) {
458 create_dataset_to_point_mapping(rtree, ccplx, point_src);
459 if (output_centers && output_corners) {
460 for(auto& cell: cells)
461 for(auto& vert: rtree.ccc_get_vertexes(cell))
462 ccplx.add_cell(cc_t::cell_kind_t::POINT, {add_node(ccplx, rtree, vert)});
463 } else if (output_centers) {
464 for(auto& cell: cells)
465 ccplx.add_cell(cc_t::cell_kind_t::POINT, {add_node(ccplx, rtree, cell)});
466 } else if (output_corners) {
467 for(auto& cell: cells)
468 for(auto& vert: rtree.ccc_get_corners(cell))
469 ccplx.add_cell(cc_t::cell_kind_t::POINT, {add_node(ccplx, rtree, vert)});
470 } else {
471 std::cout << "WARNING: construct_geometry_points: Both output_centers & output_corners are FALSE. No geometry created!" << std::endl;
472 return 1;
473 }
474 return 0;
475 }
476 //--------------------------------------------------------------------------------------------------------------------------------------------------------
477 /** @overload */
478 static int construct_geometry_points(cc_t& ccplx,
479 const rt_t& rtree,
480 val_src_lst_t point_src,
481 bool output_centers,
482 bool output_corners
483 ) {
484 return construct_geometry_points(ccplx, rtree, rtree.get_leaf_cells(), point_src, output_centers, output_corners);
485 }
486 //--------------------------------------------------------------------------------------------------------------------------------------------------------
487 /** Populate a MR_cell_cplx object from data in a MR_rect_tree object.
488
489 The resulting geometric structure in the MR_cell_cplx object will consist of 'rectangular' cell types (points, segments, rectangles, & hexahedra).
490
491 @verbatim
492 | Geom | Dom Dim | Out Dim | Result |
493 |------------+---------+---------+--------------------|
494 | RECTANGLES | 1-3 | 0 | Cell Corner Points |
495 | RECTANGLES | 2-3 | 1 | Cell Edges |
496 | RECTANGLES | 2 | 2 | 2D Rectangles |
497 | RECTANGLES | 3 | 2 | Cell Faces |
498 | RECTANGLES | 3 | 3 | Solid Hexahedra |
499 @endverbatim
500
501 @param ccplx The MR_cell_cplx to populate with geometry
502 @param rtree The MR_rect_tree with source data
503 @param cells List of tree cells from which to construct geometry
504 @param output_dimension Parts of cells to output
505 @param point_src Point sources
506 @param degenerate_fallback If the rectangle is degenerate, try and make a triangle. (only works for cc_t::cell_kind_t::QUAD) */
507 static int construct_geometry_rects(cc_t& ccplx,
508 const rt_t& rtree,
509 rt_diti_list_t cells,
510 int output_dimension,
511 val_src_lst_t point_src,
512 bool degenerate_fallback = true
513 ) {
514 create_dataset_to_point_mapping(rtree, ccplx, point_src);
515 for(auto& cell: cells) {
516 std::vector<cc_node_idx_t> cnr_pti;
517 rt_diti_list_t corners = rtree.ccc_get_corners(cell);
518 for(auto& corner: corners) {
519 cc_node_idx_t pnti = add_node(ccplx, rtree, corner);
520 cnr_pti.push_back(pnti);
521 }
522 if (rtree.domain_dimension == 1) {
523 ccplx.add_cell(cc_t::cell_kind_t::SEGMENT, {cnr_pti[0], cnr_pti[1]}, output_dimension);
524 } else if (rtree.domain_dimension == 2) {
525 const std::array<int, 4> p = {0, 1, 3, 2};
526 bool try_harder = !(ccplx.add_cell(cc_t::cell_kind_t::QUAD, {cnr_pti[0], cnr_pti[1], cnr_pti[3], cnr_pti[2]}, output_dimension));
527 if ( degenerate_fallback && try_harder) { // Try for a triangle if we have a NaN point or an adjacent pair of duplicate points
528 for(int i=0; i<4; i++) {
529 if ((cnr_pti[p[i]] < 0) || (cnr_pti[p[(i+0)%4]] == cnr_pti[p[(i+1)%4]])) {
530 ccplx.add_cell(cc_t::cell_kind_t::TRIANGLE, {cnr_pti[p[(i+1)%4]], cnr_pti[p[(i+2)%4]], cnr_pti[p[(i+3)%4]]}, output_dimension);
531 break;
532 }
533 }
534 }
535 } else { // if(rtree.domain_dimension == 3) {
536 ccplx.add_cell(cc_t::cell_kind_t::HEXAHEDRON,
537 {cnr_pti[0], cnr_pti[1], cnr_pti[3], cnr_pti[2],
538 cnr_pti[4], cnr_pti[5], cnr_pti[7], cnr_pti[6]},
539 output_dimension);
540 }
541 }
542 return 0;
543 }
544 //--------------------------------------------------------------------------------------------------------------------------------------------------------
545 /** @overload */
546 static int construct_geometry_rects(cc_t& ccplx,
547 const rt_t& rtree,
548 int output_dimension,
549 val_src_lst_t point_src,
550 bool degenerate_fallback = true
551 ) {
552 return construct_geometry_rects(ccplx, rtree, rtree.get_leaf_cells(), output_dimension, point_src, degenerate_fallback);
553 }
554 //@}
555
556
557 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
558 /** @name Function Adapters */
559 //@{
560 //--------------------------------------------------------------------------------------------------------------------------------------------------------
561 /** Adapt a MR_rect_tree::drpt2rrpt_func_t (sample function) to a MR_cell_cplx::p2data_func_t (Point Data Transform).
562 @param func The function to adapt
563 @param pd Point data to be passed to func. */
565 cc_node_data_t pd) {
566 rt_drpt_t xpt = node_data_to_drpt(pd);
567 return rt_pnt_to_cc_pnt(xpt, func(xpt));
568 }
569 //--------------------------------------------------------------------------------------------------------------------------------------------------------
570 /** Adapt a MR_rect_tree::drpt2real_func_t (Domain Point SDF) to MR_cell_cplx::p2real_func_t (Point Data SDF).
571 @param func The function to adapt
572 @param pd Point data to be passed to func. */
574 cc_node_data_t pd) {
575 return static_cast<cc_uft_t>(func(node_data_to_drpt(pd)));
576 }
577 //--------------------------------------------------------------------------------------------------------------------------------------------------------
578 /** Evalate an MR_cell_cplx::p2real_func_t (Point Data SDF) function from a MR_rect_tree::drpt2rrpt_func_t (Sample Function) and level data.
579 @param range_index Index into range of origional sample function
580 @param level Level to check range element aginst
581 @param func The function to adapt
582 @param pd Point data to be passed to func. */
583 inline static cc_uft_t tsampf_to_clcdf(int range_index,
584 cc_uft_t level,
586 cc_node_data_t pd) {
587 if constexpr (rt_t::domain_dimension == 1) {
588 return static_cast<cc_uft_t>(func(node_data_to_drpt(pd))) - level;
589 } else {
590 return static_cast<cc_uft_t>(func(node_data_to_drpt(pd))[range_index]) - level;
591 }
592 }
593 //@}
594
595 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
596 /** @name Mathematical Tools */
597 //@{
598 //--------------------------------------------------------------------------------------------------------------------------------------------------------
599 /** Drop cells from a MR_cell_cplx object using an MR_rect_tree domain SDF function.
600 */
601 inline static int cull_cc_cells_on_domain_sdf_boundry(cc_t& ccplx,
602 rt_drpt2real_func_t sdf_func) {
603 return ccplx.cull_cells([&ccplx, &sdf_func](cc_cell_verts_t c) { return ccplx.cell_near_sdf_boundry(c, [&sdf_func](cc_node_data_t pd) { return (tsdf_to_csdf(sdf_func, pd)); }); });
604 }
605
606
607 //@}
608 };
609}
610
611#define MJR_INCLUDE_MR_rt_to_cc
612#endif
Tessellates a MR_rect_tree object, and places the result into an MR_cell_cplx object.
std::variant< int, cc_uft_t > iorf_t
Type to hold an integer or float.
cc_t::node_data_t cc_node_data_t
static cc_uft_t tsdf_to_csdf(rt_drpt2real_func_t func, cc_node_data_t pd)
Adapt a MR_rect_tree::drpt2real_func_t (Domain Point SDF) to MR_cell_cplx::p2real_func_t (Point Data ...
rt_t::drpt2real_func_t rt_drpt2real_func_t
static int construct_geometry_rects(cc_t &ccplx, const rt_t &rtree, int output_dimension, val_src_lst_t point_src, bool degenerate_fallback=true)
This is an overloaded member function, provided for convenience. It differs from the above function o...
rt_t::diti_t rt_diti_t
cc_t::cell_verts_t cc_cell_verts_t
static int construct_geometry_fans(cc_t &ccplx, const rt_t &rtree, rt_diti_list_t cells, int output_dimension, val_src_lst_t point_src, rt_drpt2rrpt_func_t func=nullptr)
Populate attached MR_cell_cplx object from data in attached MR_rect_tree object.
rt_t::diti_list_t rt_diti_list_t
std::tuple< val_src_spc_t, iorf_t > val_src_t
Type used to hold a description of how to extract a scalar value from a tree object.
static int construct_geometry_points(cc_t &ccplx, const rt_t &rtree, rt_diti_list_t cells, val_src_lst_t point_src, bool output_centers, bool output_corners)
Populate attached MR_cell_cplx object from data in attached MR_rect_tree object.
static cc_node_idx_t add_node(cc_t &ccplx, rt_drpt_t dom_pnt, rt_rrpt_t rng_pnt)
Given rt coordinates, extract point/scalar/vector data, and add point/data to cc.
static int construct_geometry_fans(cc_t &ccplx, const rt_t &rtree, int output_dimension, val_src_lst_t point_src, rt_drpt2rrpt_func_t func=nullptr)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static cc_node_idx_t add_node(cc_t &ccplx, const rt_t &rtree, rt_diti_t diti)
Given rt coordinates, extract point/scalar/vector data, and add point/data to cc.
static cc_node_data_t rt_pnt_to_cc_pnt(rt_drpt_t dom_pnt, rt_rrpt_t rng_pnt)
Given rt_t domain & range points, produce a cc_t point data vector.
static int construct_geometry_rects(cc_t &ccplx, const rt_t &rtree, rt_diti_list_t cells, int output_dimension, val_src_lst_t point_src, bool degenerate_fallback=true)
Populate a MR_cell_cplx object from data in a MR_rect_tree object.
static int construct_geometry_points(cc_t &ccplx, const rt_t &rtree, val_src_lst_t point_src, bool output_centers, bool output_corners)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static cc_node_idx_t nan_edge_solver(cc_t &ccplx, const rt_t &rtree, cc_node_idx_t good_point_ccplx_index, rt_diti_t good_point_rtree_index, rt_diti_t sick_point_rtree_index, rt_drpt2rrpt_func_t func, cc_uft_t solver_epsilon=cc_t::epsilon/100)
Given an edge with one good point and one NaN point, find the longest segment from the good point tow...
static void create_dataset_to_point_mapping(const rt_t &rtree, cc_t &ccplx, const val_src_lst_t &rt_dil)
Create approprate inputs for ccplx.create_dataset_to_point_mapping() from tree data.
cc_t::node_idx_t cc_node_idx_t
static int cull_cc_cells_on_domain_sdf_boundry(cc_t &ccplx, rt_drpt2real_func_t sdf_func)
Drop cells from a MR_cell_cplx object using an MR_rect_tree domain SDF function.
std::vector< val_src_t > val_src_lst_t
A list of val_src_t objects.
static rt_drpt_t node_data_to_drpt(const cc_node_data_t &pd)
Transform a MR_cell_cplx::node_data_t value into MR_rect_tree::drpt_t.
rt_t::rrpt_t rt_rrpt_t
cc_t::uft_t cc_uft_t
cc_t::node_data_idx_lst_t cc_node_data_idx_lst_t
rt_t::drpt2rrpt_func_t rt_drpt2rrpt_func_t
val_src_spc_t
Specify a source space for a data index.
static cc_node_data_t tsampf_to_cdatf(rt_drpt2rrpt_func_t func, cc_node_data_t pd)
Adapt a MR_rect_tree::drpt2rrpt_func_t (sample function) to a MR_cell_cplx::p2data_func_t (Point Data...
rt_t::drpt_t rt_drpt_t
static cc_uft_t tsampf_to_clcdf(int range_index, cc_uft_t level, rt_drpt2rrpt_func_t func, cc_node_data_t pd)
Evalate an MR_cell_cplx::p2real_func_t (Point Data SDF) function from a MR_rect_tree::drpt2rrpt_func_...
cc_t::node_idx_list_t cc_node_idx_list_t
static int rt_rng_idx_to_pd_idx(int tree_range_index)
Convert a MR_rect_tree range index into an index for a point data array.