Tatooine
isosurface.h
Go to the documentation of this file.
1#ifndef TATOOINE_ISOSURFACE_H
2#define TATOOINE_ISOSURFACE_H
3//==============================================================================
4#include <cassert>
5#include <string>
6#include <vector>
7#ifndef NDEBUG
8#include <mutex>
9#endif
10
11#include <tatooine/field.h>
12#include <tatooine/for_loop.h>
15#include <tatooine/tensor.h>
17#include <tatooine/utility.h>
18//==============================================================================
19namespace tatooine {
20//==============================================================================
23template <
24 typename XDomain, typename YDomain, typename ZDomain, arithmetic Isolevel,
25 invocable<
26 std::size_t, std::size_t, std::size_t,
27 vec<typename rectilinear_grid<XDomain, YDomain, ZDomain>::real_type,
28 3> >
29 GetScalars>
30auto isosurface(GetScalars&& get_scalars,
32 Isolevel const isolevel) {
33 using real_type =
35 using pos_type = vec<real_type, 3>;
37
38#if defined(NDEBUG) && defined(TATOOINE_OPENMP_AVAILABLE)
39 std::mutex mutex;
40#endif
41 auto process_cube = [&](auto ix, auto iy, auto iz) {
42 auto vertlist = make_array<pos_type, 12>();
43 using vertlist_type = decltype(vertlist);
44 using vertlist_size_type = typename vertlist_type::size_type;
45 std::array p{
46 g.vertex_at(ix, iy, iz + 1), g.vertex_at(ix + 1, iy, iz + 1),
47 g.vertex_at(ix + 1, iy, iz), g.vertex_at(ix, iy, iz),
48 g.vertex_at(ix, iy + 1, iz + 1), g.vertex_at(ix + 1, iy + 1, iz + 1),
49 g.vertex_at(ix + 1, iy + 1, iz), g.vertex_at(ix, iy + 1, iz)};
50
51 decltype(auto) s0 = get_scalars(ix, iy, iz + 1, p[0]);
52 decltype(auto) s1 = get_scalars(ix + 1, iy, iz + 1, p[1]);
53 decltype(auto) s2 = get_scalars(ix + 1, iy, iz, p[2]);
54 decltype(auto) s3 = get_scalars(ix, iy, iz, p[3]);
55 decltype(auto) s4 = get_scalars(ix, iy + 1, iz + 1, p[4]);
56 decltype(auto) s5 = get_scalars(ix + 1, iy + 1, iz + 1, p[5]);
57 decltype(auto) s6 = get_scalars(ix + 1, iy + 1, iz, p[6]);
58 decltype(auto) s7 = get_scalars(ix, iy + 1, iz, p[7]);
59
60 unsigned int cube_index = 0;
61 if (s0 < isolevel) {
62 cube_index |= 1;
63 }
64 if (s1 < isolevel) {
65 cube_index |= 2;
66 }
67 if (s2 < isolevel) {
68 cube_index |= 4;
69 }
70 if (s3 < isolevel) {
71 cube_index |= 8;
72 }
73 if (s4 < isolevel) {
74 cube_index |= 16;
75 }
76 if (s5 < isolevel) {
77 cube_index |= 32;
78 }
79 if (s6 < isolevel) {
80 cube_index |= 64;
81 }
82 if (s7 < isolevel) {
83 cube_index |= 128;
84 }
85
86 // Cube is entirely in/out of the surface
87 if (marchingcubes_lookup::edge_table[cube_index] == 0) {
88 return;
89 }
90
91 // Find the vertices where the surface intersects the cube
92 if (marchingcubes_lookup::edge_table[cube_index] & 1) {
93 real_type const s = (isolevel - s0) / (s1 - s0);
94 vertlist[0] = p[0] * (1 - s) + p[1] * s;
95 }
96 if (marchingcubes_lookup::edge_table[cube_index] & 2) {
97 real_type const s = (isolevel - s1) / (s2 - s1);
98 vertlist[1] = p[1] * (1 - s) + p[2] * s;
99 }
100 if (marchingcubes_lookup::edge_table[cube_index] & 4) {
101 real_type const s = (isolevel - s2) / (s3 - s2);
102 vertlist[2] = p[2] * (1 - s) + p[3] * s;
103 }
104 if (marchingcubes_lookup::edge_table[cube_index] & 8) {
105 real_type const s = (isolevel - s3) / (s0 - s3);
106 vertlist[3] = p[3] * (1 - s) + p[0] * s;
107 }
108 if (marchingcubes_lookup::edge_table[cube_index] & 16) {
109 real_type const s = (isolevel - s4) / (s5 - s4);
110 vertlist[4] = p[4] * (1 - s) + p[5] * s;
111 }
112 if (marchingcubes_lookup::edge_table[cube_index] & 32) {
113 real_type const s = (isolevel - s5) / (s6 - s5);
114 vertlist[5] = p[5] * (1 - s) + p[6] * s;
115 }
116 if (marchingcubes_lookup::edge_table[cube_index] & 64) {
117 real_type const s = (isolevel - s6) / (s7 - s6);
118 vertlist[6] = p[6] * (1 - s) + p[7] * s;
119 }
120 if (marchingcubes_lookup::edge_table[cube_index] & 128) {
121 real_type const s = (isolevel - s7) / (s4 - s7);
122 vertlist[7] = p[7] * (1 - s) + p[4] * s;
123 }
124 if (marchingcubes_lookup::edge_table[cube_index] & 256) {
125 real_type const s = (isolevel - s0) / (s4 - s0);
126 vertlist[8] = p[0] * (1 - s) + p[4] * s;
127 }
128 if (marchingcubes_lookup::edge_table[cube_index] & 512) {
129 real_type const s = (isolevel - s1) / (s5 - s1);
130 vertlist[9] = p[1] * (1 - s) + p[5] * s;
131 }
132 if (marchingcubes_lookup::edge_table[cube_index] & 1024) {
133 real_type const s = (isolevel - s2) / (s6 - s2);
134 vertlist[10] = p[2] * (1 - s) + p[6] * s;
135 }
136 if (marchingcubes_lookup::edge_table[cube_index] & 2048) {
137 real_type const s = (isolevel - s3) / (s7 - s3);
138 vertlist[11] = p[3] * (1 - s) + p[7] * s;
139 }
140
141#if defined(NDEBUG) && defined(TATOOINE_OPENMP_AVAILABLE)
142 {
143 std::lock_guard lock{mutex};
144#endif
145 // create the triangle
146 for (std::size_t i = 0;
147 marchingcubes_lookup::tri_table[cube_index][i] != -1; i += 3) {
148 iso_volume.insert_simplex(
149 iso_volume.insert_vertex(vertlist[static_cast<vertlist_size_type>(
150 marchingcubes_lookup::tri_table[cube_index][i])]),
151 iso_volume.insert_vertex(vertlist[static_cast<vertlist_size_type>(
152 marchingcubes_lookup::tri_table[cube_index][i + 2])]),
153 iso_volume.insert_vertex(vertlist[static_cast<vertlist_size_type>(
154 marchingcubes_lookup::tri_table[cube_index][i + 1])]));
155 }
156#if defined(NDEBUG) && defined(TATOOINE_OPENMP_AVAILABLE)
157 }
158#endif
159 };
160#if defined(NDEBUG) && defined(TATOOINE_OPENMP_AVAILABLE)
161 for_loop(process_cube, execution_policy::parallel, g.size(0) - 1,
162 g.size(1) - 1, g.size(2) - 1);
163#else
164 for_loop(process_cube, execution_policy::sequential, g.size(0) - 1,
165 g.size(1) - 1, g.size(2) - 1);
166#endif
167 return iso_volume;
168}
169//------------------------------------------------------------------------------
170template <arithmetic Real, typename Indexing, arithmetic BBReal,
171 arithmetic Isolevel>
174 Isolevel const isolevel) {
175 assert(data.num_dimensions() == 3);
176 return isosurface(
177 [&](auto ix, auto iy, auto iz, auto const & /*ps*/) -> auto const& {
178 return data(ix, iy, iz);
179 },
180 rectilinear_grid{linspace{bb.min(0), bb.max(0), data.size(0)},
181 linspace{bb.min(1), bb.max(1), data.size(1)},
182 linspace{bb.min(2), bb.max(2), data.size(2)}},
183 isolevel);
184}
185//------------------------------------------------------------------------------
186template <arithmetic Real, typename Indexing, typename MemLoc, std::size_t XRes,
187 std::size_t YRes, std::size_t ZRes, arithmetic BBReal,
188 arithmetic Isolevel>
191 axis_aligned_bounding_box<BBReal, 3> const& bb, Isolevel const isolevel) {
192 return isosurface(
193 [&](auto ix, auto iy, auto iz, auto const & /*ps*/) -> auto const& {
194 return data(ix, iy, iz);
195 },
196 rectilinear_grid{linspace{bb.min(0), bb.max(0), data.size(0)},
197 linspace{bb.min(1), bb.max(1), data.size(1)},
198 linspace{bb.min(2), bb.max(2), data.size(2)}},
199 isolevel);
200}
201//------------------------------------------------------------------------------
202template <typename Field, arithmetic FieldReal, typename XDomain,
203 typename YDomain, typename ZDomain, arithmetic Isolevel,
204 arithmetic TReal = int>
207 Isolevel const isolevel, TReal const t = 0) {
208 return isosurface(
209 [&](integral auto const /*ix*/, integral auto const /*iy*/,
210 integral auto const /*iz*/, auto const& pos) { return sf(pos, t); },
211 g, isolevel);
212}
213//------------------------------------------------------------------------------
214template <typename Grid, arithmetic T, bool HasNonConstReference>
216 Grid, T, HasNonConstReference> const& data,
217 arithmetic auto const isolevel) {
218 return isosurface([&](integral auto const ix, integral auto const iy,
219 integral auto const iz,
220 auto const& /*pos*/) { return data(ix, iy, iz); },
221 data.grid(), isolevel);
222}
223//==============================================================================
224} // namespace tatooine
225//==============================================================================
226#endif
Definition: dynamic_multidim_array.h:18
auto size() const -> auto const &
Definition: dynamic_multidim_size.h:107
auto num_dimensions() const
Definition: dynamic_multidim_size.h:105
Definition: rectilinear_grid.h:38
constexpr auto size(std::index_sequence< Seq... >) const
Definition: rectilinear_grid.h:347
auto vertex_at(std::index_sequence< DIs... >, std::array< Int, num_dimensions()> const &is) const -> vec< real_type, num_dimensions()>
Definition: rectilinear_grid.h:640
common_type< typename Dimensions::value_type... > real_type
Definition: rectilinear_grid.h:46
Definition: static_multidim_array.h:19
static auto constexpr size()
Definition: static_multidim_array.h:30
Definition: concepts.h:33
Definition: concepts.h:21
static constexpr sequential_t sequential
Definition: tags.h:63
static constexpr parallel_t parallel
Definition: tags.h:60
constexpr std::array< std::array< int, 16 >, 256 > tri_table
Definition: marchingcubeslookuptable.h:35
static constexpr std::array< int, 256 > edge_table
Definition: marchingcubeslookuptable.h:9
Definition: algorithm.h:6
auto isosurface(GetScalars &&get_scalars, rectilinear_grid< XDomain, YDomain, ZDomain > const &g, Isolevel const isolevel)
Indexing and lookup map from http://paulbourke.net/geometry/polygonise/.
Definition: isosurface.h:30
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: axis_aligned_bounding_box.h:103
auto constexpr max() const -> auto const &
Definition: axis_aligned_bounding_box.h:156
auto constexpr min() const -> auto const &
Definition: axis_aligned_bounding_box.h:151
Definition: field.h:134
Definition: linspace.h:26
auto insert_vertex(arithmetic auto const ... comps)
Definition: unstructured_simplicial_grid.h:333
auto insert_simplex(Handles const ... handles)
Definition: unstructured_simplicial_grid.h:427
Definition: unstructured_triangular_grid.h:10
Definition: vec.h:12