cgv
Loading...
Searching...
No Matches
marching_cubes.h
1#pragma once
2
3#include <vector>
4#include <deque>
5#include <cgv/utils/progression.h>
6#include <cgv/math/fvec.h>
7#include <cgv/math/mfunc.h>
8#include <cgv/media/axis_aligned_box.h>
9#include <cgv/media/mesh/streaming_mesh.h>
10
11#include <cgv/media/lib_begin.h>
12
13namespace cgv {
14 namespace media {
15 namespace mesh {
16
17extern CGV_API int get_nr_cube_triangles(int idx);
18extern CGV_API void put_cube_triangle(int idx, int t, int& vi, int& vj, int& vk);
19
20
22template <typename T>
24{
25 unsigned int resx, resy;
26 std::vector<T> values;
27 std::vector<bool> flags;
28 std::vector<int> indices;
30 slice_info(unsigned int _resx, unsigned int _resy) : resx(_resx), resy(_resy) {
31 unsigned int n = resx*resy;
32 values.resize(n);
33 flags.resize(n);
34 indices.resize(4 * n);
35 }
37 void init() {
38 int n = resx*resy;
39 for (int i = 0; i < 4 * n; ++i)
40 indices[i] = -1;
41 }
43 bool flag(int x, int y) const { return flags[y*resx + x]; }
45 int get_bit_code(int x, int y, int step = 1) const {
46 int i = y*resx + x;
47 return (flags[i] ? 1 : 0) + (flags[i + step] ? 2 : 0) +
48 (flags[i + step*(resx + 1)] ? 4 : 0) + (flags[i + step*resx] ? 8 : 0);
49 }
51 const T& value(int x, int y) const { return values[y*resx + x]; }
52 T& value(int x, int y) { return values[y*resx + x]; }
54 void set_value(int x, int y, T value, T iso_value) {
55 int i = y*resx + x;
56 values[i] = value;
57 flags[i] = value > iso_value;
58 }
60 const int& index(int x, int y, int e) const { return indices[4 * (y*resx + x) + e]; }
61 int& index(int x, int y, int e) { return indices[4 * (y*resx + x) + e]; }
63 const int& snap_index(int x, int y) const { return indices[4 * (y*resx + x) + 3]; }
64 int& snap_index(int x, int y) { return indices[4 * (y*resx + x) + 3]; }
65};
66
68template <typename X, typename T>
70{
71public:
77private:
78 pnt_type p;
79 vec_type d;
80 T iso_value;
81protected:
82 X epsilon;
83 X grid_epsilon;
84public:
87 const X& _grid_epsilon = 0.01f,
88 const X& _epsilon = 1e-6f) : epsilon(_epsilon), grid_epsilon(_grid_epsilon)
89 {
91 }
93 void construct_vertex(slice_info<T> *info_ptr_1, int i_1, int j_1, int e,
94 slice_info<T> *info_ptr_2, int i_2, int j_2)
95 {
96 // read values at edge ends
97 T v_1 = info_ptr_1->value(i_1, j_1);
98 T v_2 = info_ptr_2->value(i_2, j_2);
99 // from values compute affin location
100 X f = (fabs(v_2 - v_1) > epsilon) ? (X)(iso_value - v_1) / (v_2 - v_1) : (X) 0.5;
101 // check whether to snap to edge start
103 pnt_type q = p;
104 if (f < grid_epsilon) {
105 int vj = info_ptr_1->snap_index(i_1, j_1);
106 if (vj != -1) {
107 info_ptr_1->index(i_1, j_1, e) = vj;
108 return;
109 }
110 info_ptr_1->snap_index(i_1, j_1) = vi;
111 q(e) -= d(e);
112 }
113 else if (1 - f < grid_epsilon) {
114 int vj = info_ptr_2->snap_index(i_2, j_2);
115 if (vj != -1) {
116 info_ptr_1->index(i_1, j_1, e) = vj;
117 return;
118 }
119 info_ptr_2->snap_index(i_2, j_2) = vi;
120 }
121 else
122 q(e) -= (1 - f)*d(e);
123
124 info_ptr_1->index(i_1, j_1, e) = vi;
125 this->new_vertex(q);
126 }
127
129 template <typename Eval, typename Valid>
130 void extract_impl(const T& _iso_value,
131 const axis_aligned_box<X, 3>& box,
132 unsigned int resx, unsigned int resy, unsigned int resz,
133 const Eval& eval, const Valid& valid, bool show_progress = false)
134 {
135 // prepare private members
136 p = box.get_min_pnt();
137 d = box.get_extent();
138 d(0) /= (resx - 1); d(1) /= (resy - 1); d(2) /= (resz - 1);
139 iso_value = _iso_value;
140
141 // prepare progression
143 if (show_progress) prog.init("extraction", resz, 10);
144
145 // construct two slice infos
146 slice_info<T> slice_info_1(resx, resy), slice_info_2(resx, resy);
147 slice_info<T> *slice_info_ptrs[2] = { &slice_info_1, &slice_info_2 };
148
149 // iterate through all slices
150 unsigned int nr_vertices[3] = { 0, 0, 0 };
151 unsigned int i, j, k, n;
152 for (k = 0; k < resz; ++k, p(2) += d(2)) {
154 // evaluate function on next slice and construct slice interior vertices
155 slice_info<T> *info_ptr = slice_info_ptrs[k & 1];
156 info_ptr->init();
157 for (j = 0, p(1) = box.get_min_pnt()(1); j < resy; ++j, p(1) += d(1))
158 for (i = 0, p(0) = box.get_min_pnt()(0); i < resx; ++i, p(0) += d(0)) {
159 T v = eval(i, j, k, p);
160 info_ptr->set_value(i, j, v, iso_value);
161 if (valid(v)) {
162 if (i > 0 && info_ptr->flag(i - 1, j) != info_ptr->flag(i, j) && valid(info_ptr->value(i - 1, j)))
163 construct_vertex(info_ptr, i - 1, j, 0, info_ptr, i, j);
164 if (j > 0 && info_ptr->flag(i, j - 1) != info_ptr->flag(i, j) && valid(info_ptr->value(i, j - 1)))
165 construct_vertex(info_ptr, i, j - 1, 1, info_ptr, i, j);
166 }
167 }
168 // show progression
169 if (show_progress)
170 prog.step();
171 // if this is the first considered slice, construct the next one
172 if (k != 0) {
173 // get info of previous slice
174 slice_info<T> *prev_info_ptr = slice_info_ptrs[1 - (k & 1)];
175 // construct vertices on edges between previous and new slice
176 for (j = 0, p(1) = box.get_min_pnt()(1); j < resy; ++j, p(1) += d(1))
177 for (i = 0, p(0) = box.get_min_pnt()(0); i < resx; ++i, p(0) += d(0))
178 if (prev_info_ptr->flag(i, j) != info_ptr->flag(i, j) && valid(prev_info_ptr->value(i, j)) && valid(info_ptr->value(i, j)))
179 construct_vertex(prev_info_ptr, i, j, 2, info_ptr, i, j);
180
181 // construct triangles
182 for (j = 0; j < resy - 1; ++j) {
183 for (i = 0; i < resx - 1; ++i) {
184 // compute the bit index for the current cube
185 int idx = prev_info_ptr->get_bit_code(i, j) +
186 16 * info_ptr->get_bit_code(i, j);
187 // skip empty cubes
188 if (idx == 0 || idx == 255)
189 continue;
190 // set edge vertices
191 int vis[12] = {
192 prev_info_ptr->index(i, j, 0),
193 prev_info_ptr->index(i + 1, j, 1),
194 prev_info_ptr->index(i, j + 1, 0),
195 prev_info_ptr->index(i, j, 1),
196 info_ptr->index(i, j, 0),
197 info_ptr->index(i + 1, j, 1),
198 info_ptr->index(i, j + 1, 0),
199 info_ptr->index(i, j, 1),
200 prev_info_ptr->index(i, j, 2),
201 prev_info_ptr->index(i + 1, j, 2),
202 prev_info_ptr->index(i, j + 1, 2),
203 prev_info_ptr->index(i + 1, j + 1, 2)
204 };
205 // lookup triangles and construct them
206 int n = get_nr_cube_triangles(idx);
207 for (int t = 0; t < n; ++t) {
208 int vi, vj, vk;
209 put_cube_triangle(idx, t, vi, vj, vk);
210 vi = vis[vi];
211 vj = vis[vj];
212 vk = vis[vk];
213 if (vi == -1 || vj == -1 || vk == -1)
214 continue;
215 if ((vi != vj) && (vi != vk) && (vj != vk))
216 base_type::new_triangle(vk, vj, vi);
217 }
218 }
219 }
220 }
221 n = (int)base_type::get_nr_vertices() - n;
222 nr_vertices[k % 3] = n;
223 n = nr_vertices[(k + 2) % 3];
224 if (n > 0)
226 }
227 }
228};
229
230template <typename T>
232{
233 bool operator () (const T& v) const { return true; }
234};
235
236#include <limits>
237
238template <typename T>
240{
241 bool operator () (const T& v) const { return v != std::numeric_limits<T>::max(); }
242};
243
244template <typename T>
246{
247 bool operator () (const T& v) const { return !std::isnan(v); }
248};
249
251template <typename X, typename T>
253{
254public:
255 // GCC does not examine the base class scope for templates,
256 // so they must be explicitely redefined
262
263 const cgv::math::v3_func<X, T>& func;
267 const X& _grid_epsilon = 0.01f,
268 const X& _epsilon = 1e-6f) : marching_cubes_base<X, T>(_smcbh, _grid_epsilon, _epsilon), func(_func)
269 {
270 }
271 T operator () (unsigned i, unsigned j, unsigned k, const pnt_type& p) const {
272 return func.evaluate(p.to_vec());
273 }
274 void extract(const T& _iso_value,
275 const axis_aligned_box<X, 3>& box,
276 unsigned int resx, unsigned int resy, unsigned int resz,
277 bool show_progress = false)
278 {
279 always_valid<T> valid;
280 this->extract_impl(_iso_value, box, resx, resy, resz, *this, valid, show_progress);
281 }
282};
283
284 }
285 }
286}
287
288#include <cgv/config/lib_end.h>
A vector with zero based index.
Definition fvec.h:26
virtual T evaluate(const pnt_type &p) const =0
interface for evaluation of the multivariate function
specialization of a multivariate function to three independent variables, which only reimplements the...
Definition mfunc.h:63
An axis aligned box, defined by to points: min and max.
fvec_type get_extent() const
return a vector with the extents in the different dimensions
const fpnt_type & get_min_pnt() const
return a const reference to corner 0
class used to perform the marching cubes algorithm
marching_cubes_base(streaming_mesh_callback_handler *_smcbh, const X &_grid_epsilon=0.01f, const X &_epsilon=1e-6f)
construct marching cubes object
cgv::math::fvec< X, 3 > vec_type
vectors must have three components
cgv::math::fvec< X, 3 > pnt_type
points must have three components
void extract_impl(const T &_iso_value, const axis_aligned_box< X, 3 > &box, unsigned int resx, unsigned int resy, unsigned int resz, const Eval &eval, const Valid &valid, bool show_progress=false)
extract iso surface and send triangles to marching cubes handler
void construct_vertex(slice_info< T > *info_ptr_1, int i_1, int j_1, int e, slice_info< T > *info_ptr_2, int i_2, int j_2)
construct a new vertex on an edge
class used to perform the marching cubes algorithm
marching_cubes_base< X, T >::pnt_type pnt_type
points must have three components
marching_cubes_base< X, T >::vec_type vec_type
vectors must have three components
marching_cubes(const cgv::math::v3_func< X, T > &_func, streaming_mesh_callback_handler *_smcbh, const X &_grid_epsilon=0.01f, const X &_epsilon=1e-6f)
construct marching cubes object
class used to perform the marching cubes algorithm
void set_callback_handler(streaming_mesh_callback_handler *_smcbh)
set a new callback handler
unsigned int get_nr_vertices() const
return the number of vertices
unsigned int new_vertex(const pnt_type &p)
add a new vertex with the given location and call the callback of the callback handler
void new_triangle(unsigned int vi, unsigned int vj, unsigned int vk)
construct a new triangle by calling the new polygon method of the callback handler
void drop_vertices(unsigned int n)
drop n vertices from the front of the deque
the cgv namespace
Definition print.h:11
data structure for the information that is cached per volume slice
void set_value(int x, int y, T value, T iso_value)
set value and flag at once
pure abstract interface to handle callbacks of a streaming mesh
progression provides a simple possibility to show progression of process in console.
Definition progression.h:14
void step()
next iteration
void init(const std::string &process, size_t total, int count)
reinitialize