cgv
Loading...
Searching...
No Matches
dual_contouring.h
1#pragma once
2
3#include <vector>
4#include <deque>
5#include <cgv/utils/progression.h>
6#include <cgv/math/qem.h>
7#include <cgv/math/mfunc.h>
8#include <cgv/media/axis_aligned_box.h>
9#include "streaming_mesh.h"
10
11namespace cgv {
12 namespace media {
13 namespace mesh {
14
16template <typename T>
17struct cell_info {
20 int count;
21};
22
23
25template <typename T>
27{
28 unsigned int resx, resy;
29 std::vector<T> values;
30 std::vector<bool> flags;
31 std::vector<int> indices;
34 std::vector<cell_info<T> > cell_infos;
36 dc_slice_info(unsigned int _resx, unsigned int _resy) : resx(_resx), resy(_resy) {
37 unsigned int n = resx*resy;
38 values.resize(n);
39 flags.resize(n);
40 indices.resize(n);
41 cell_infos.resize(n);
42 }
44 void init() {
45 int n = resx*resy;
46 for (int i=0; i<n; ++i) {
47 indices[i] = -1;
48 cell_infos[i].Q = qem_type();
49 cell_infos[i].count = 0;
50 cell_infos[i].center = pnt_type();
51 }
52 }
54 bool flag(int x, int y) const { return flags[y*resx+x]; }
56 const T& value(int x, int y) const { return values[y*resx+x]; }
57 T& value(int x, int y) { return values[y*resx+x]; }
59 void set_value(int x, int y, T value, T iso_value) {
60 int i = y*resx+x;
61 values[i] = value;
62 flags[i] = value>iso_value;
63 }
65 const int& index(int x, int y) const { return indices[y*resx+x]; }
66 int& index(int x, int y) { return indices[y*resx+x]; }
68 const qem_type& get_qem(int x, int y) const { return cell_infos[y*resx+x].Q; }
69 qem_type& ref_qem(int x, int y) { return cell_infos[y*resx+x].Q; }
71 int count(int x, int y) const { return cell_infos[y*resx+x].count; }
72 int& count(int x, int y) { return cell_infos[y*resx+x].count; }
74 const pnt_type& center(int x, int y) const { return cell_infos[y*resx+x].center; }
75 pnt_type& center(int x, int y) { return cell_infos[y*resx+x].center; }
77 const cell_info<T>& info(int x, int y) const { return cell_infos[y*resx+x]; }
78 cell_info<T>& info(int x, int y) { return cell_infos[y*resx+x]; }
79};
80
82template <typename X, typename T>
84{
85public:
95private:
96 pnt_type p, minp;
97 unsigned int resx, resy, resz;
98 vec_type d;
99 T iso_value;
100protected:
101 const cgv::math::v3_func<X,T>& func;
102 X epsilon;
103 unsigned int max_nr_iters;
104 X consistency_threshold;
105public:
109 const X& _consistency_threshold = 0.01f, unsigned int _max_nr_iters = 10,
110 const X& _epsilon = 1e-6f) :
111 func(_func), max_nr_iters(_max_nr_iters), consistency_threshold(_consistency_threshold), epsilon(_epsilon)
112 {
114 }
116 void compute_cell_vertex(dc_slice_info<T> *info_ptr, int i, int j)
117 {
118 if (info_ptr->count(i,j) == 0) {
119 info_ptr->index(i,j) = -1;
120 return;
121 }
122 pnt_type p_ref = info_ptr->center(i,j) / X(info_ptr->count(i,j));
123 cgv::math::vec<X> min_pnt = info_ptr->get_qem(i, j).minarg(p_ref.to_vec(), X(0.1), d.length());
124 pnt_type q(min_pnt.size(), min_pnt.data());
125 info_ptr->index(i,j) = this->new_vertex(q);
126 }
128 void generate_quad(unsigned int vi, unsigned int vj, unsigned int vk, unsigned int vl, bool reorient)
129 {
130 if (reorient)
131 base_type::new_quad(vi,vl,vk,vj);
132 else
133 base_type::new_quad(vi,vj,vk,vl);
134 }
136 void compute_edge_point(const T& _v_1, const T& _v_2, int e, const pnt_type& p, const vec_type& d,
137 pnt_type& q, vec_type& n)
138 {
139 X de = d(e);
140 T v_1 = _v_1;
141 T v_2 = _v_2;
142 pnt_type p_end = p;
143 q = p;
144 unsigned int nr_iters = 0;
145 bool finished;
146 X ref = consistency_threshold*fabs(v_2-v_1);
147 do {
148 finished = false;
149 // compute point on edge
150 X alpha;
151 if (fabs(v_2-v_1) > epsilon) {
152 finished = true;
153 alpha = (X)(iso_value-v_1)/(v_2-v_1);
154 }
155 else
156 alpha = (X) 0.5;
157
158 q(e) = p_end(e) - (1-alpha)*de;
159 // compute normal at point
160 cgv::math::vec<X> nml_vec = func.evaluate_gradient(q.to_vec());
161 n = vec_type(nml_vec.size(), nml_vec.data());
162 n.normalize();
163
164 // stop if maximum number of iterations reached
165 if (++nr_iters >= max_nr_iters)
166 break;
167 // check if gradient is in accordance with value difference
168 if (finished) {
169 if (alpha < 0 || alpha > 1) {
170 std::cout << "invalid alpha = " << alpha << std::endl;
171 }
172 X f_e = (v_2 - v_1) / de;
173 if (fabs(f_e - n(e)) > consistency_threshold*f_e) {
174 X v = func.evaluate(q.to_vec());
175 if (fabs(v-iso_value) > ref) {
176 if ((v > iso_value) == (v_2 > iso_value)) {
177 de *= alpha;
178 v_2 = v;
179 p_end(e) = q(e);
180 }
181 else {
182 v_1 = v;
183 de *= 1-alpha;
184 }
185 finished = false;
186 }
187 }
188 }
189 } while (!finished);
190 if (nr_iters > 15)
191 std::cout << "not converged after " << nr_iters << " iterations" << std::endl;
192 // if gradient does not define normal
193 if (n.length() < 1e-6) {
194 // define normal from edge direction
195 n = vec_type(0, 0, 0);
196 n(e) = T((v_1 < v_2) ? 1 : 0);
197 }
198 else
199 n.normalize();
200 }
202 void process_edge_plane(const T& v_1, const T& v_2, int e,
204 {
205 pnt_type q;
206 vec_type n;
207 compute_edge_point(v_1, v_2, e, p, d, q, n);
208 // construct qem
209 qem_type Q(q.to_vec(),n.to_vec());
210 // add qem to incident cells
211 if (C1) { if (C1->count == 0) { C1->Q = Q; C1->center = q; } else { C1->Q += Q; C1->center += q; } ++C1->count; }
212 if (C2) { if (C2->count == 0) { C2->Q = Q; C2->center = q; } else { C2->Q += Q; C2->center += q; } ++C2->count; }
213 if (C3) { if (C3->count == 0) { C3->Q = Q; C3->center = q; } else { C3->Q += Q; C3->center += q; } ++C3->count; }
214 if (C4) { if (C4->count == 0) { C4->Q = Q; C4->center = q; } else { C4->Q += Q; C4->center += q; } ++C4->count; }
215 }
217 void process_slice(dc_slice_info<T> *prev_info_ptr, dc_slice_info<T> *info_ptr)
218 {
219 unsigned int i,j;
220 info_ptr->init();
221 for (j = 0, p(1) = minp(1); j < resy; ++j, p(1) += d(1))
222 for (i = 0, p(0) = minp(0); i < resx; ++i, p(0)+=d(0)) {
223 // eval function on slice
224 info_ptr->set_value(i,j,func.evaluate(p.to_vec()),iso_value);
225 // process slice internal edges
226 if (i > 0 && info_ptr->flag(i-1,j) != info_ptr->flag(i,j))
227 process_edge_plane(info_ptr->value(i-1,j),
228 info_ptr->value(i,j), 0,
229 &info_ptr->info(i-1,j),
230 j > 0 ? &info_ptr->info(i-1,j-1) : 0,
231 prev_info_ptr ? &prev_info_ptr->info(i-1,j) : 0,
232 (j > 0 && prev_info_ptr) ? &prev_info_ptr->info(i-1,j-1) : 0);
233 if (j > 0 && info_ptr->flag(i,j-1) != info_ptr->flag(i,j))
234 process_edge_plane(info_ptr->value(i,j-1),
235 info_ptr->value(i,j), 1,
236 &info_ptr->info(i,j-1),
237 i > 0 ? &info_ptr->info(i-1,j-1) : 0,
238 prev_info_ptr ? &prev_info_ptr->info(i,j-1) : 0,
239 (i > 0 && prev_info_ptr) ? &prev_info_ptr->info(i-1,j-1) : 0);
240 }
241 }
243 void process_slab(dc_slice_info<T> *info_ptr_1, dc_slice_info<T> *info_ptr_2)
244 {
245 unsigned int i,j;
246 for (j = 0, p(1) = minp(1); j < resy; ++j, p(1) += d(1))
247 for (i = 0, p(0) = minp(0); i < resx; ++i, p(0)+=d(0)) {
248 // process slab edges
249 if (info_ptr_1->flag(i,j) != info_ptr_2->flag(i,j))
250 process_edge_plane(info_ptr_1->value(i,j),
251 info_ptr_2->value(i,j), 2,
252 &info_ptr_1->info(i,j),
253 j > 0 ? &info_ptr_1->info(i,j-1) : 0,
254 i > 0 ? &info_ptr_1->info(i-1,j) : 0,
255 (i > 0 && j > 0) ? &info_ptr_1->info(i-1,j-1) : 0);
256 // compute cell vertices
257 if (i>0 && j>0)
258 compute_cell_vertex(info_ptr_1, i-1,j-1);
259 }
260 // generate the quads of inner edges inside the slab
261 for (j = 1, p(1) = minp(1)+d(1); j < resy-1; ++j, p(1) += d(1))
262 for (i = 1, p(0) = minp(0)+d(0); i < resx-1; ++i, p(0)+=d(0))
263 if (info_ptr_1->flag(i,j) != info_ptr_2->flag(i,j))
264 generate_quad(info_ptr_1->index(i,j),
265 info_ptr_1->index(i-1,j),
266 info_ptr_1->index(i-1,j-1),
267 info_ptr_1->index(i,j-1),
268 info_ptr_1->flag(i,j));
269 }
271 void generate_slice_quads(dc_slice_info<T> *info_ptr_1, dc_slice_info<T> *info_ptr_2)
272 {
273 unsigned int i,j;
274 for (j = 1, p(1) = minp(1)+d(1); j < resy-1; ++j, p(1) += d(1))
275 for (i = 1, p(0) = minp(0)+d(0); i < resx-1; ++i, p(0)+=d(0)) {
276 if (info_ptr_2->flag(i-1,j) != info_ptr_2->flag(i,j))
277 generate_quad(info_ptr_1->index(i-1,j),
278 info_ptr_2->index(i-1,j),
279 info_ptr_2->index(i-1,j-1),
280 info_ptr_1->index(i-1,j-1),
281 info_ptr_2->flag(i-1,j));
282 if (info_ptr_2->flag(i,j-1) != info_ptr_2->flag(i,j))
283 generate_quad(info_ptr_1->index(i,j-1),
284 info_ptr_1->index(i-1,j-1),
285 info_ptr_2->index(i-1,j-1),
286 info_ptr_2->index(i,j-1),
287 info_ptr_2->flag(i,j-1));
288 }
289 }
291 void extract(const T& _iso_value,
292 const axis_aligned_box<X,3>& box,
293 unsigned int _resx, unsigned int _resy, unsigned int _resz,
294 bool show_progress = false)
295 {
296 // prepare private members
297 resx = _resx; resy = _resy; resz = _resz;
298 minp = p = box.get_min_pnt();
299 d = box.get_extent();
300 d(0) /= (resx-1); d(1) /= (resy-1); d(2) /= (resz-1);
301 iso_value = _iso_value;
302
303 // prepare progression
305 if (show_progress) prog.init("extraction", resz, 10);
306
307 // construct three slice infos
308 dc_slice_info<T> slice_info_1(resx,resy), slice_info_2(resx,resy), slice_info_3(resx,resy);
309 dc_slice_info<T> *slice_info_ptrs[3] = { &slice_info_1, &slice_info_2, &slice_info_3 };
310
311 // iterate through all slices
312 unsigned int nr_vertices[4] = { 0, 0, 0, 0 };
313 unsigned int k, n;
314
315 process_slice(0, slice_info_ptrs[0]);
316 p(2) += d(2);
317 process_slice(slice_info_ptrs[0], slice_info_ptrs[1]);
318 process_slab(slice_info_ptrs[0], slice_info_ptrs[1]);
319 p(2) += d(2);
320 // show progression
321 if (show_progress) {
322 prog.step();
323 prog.step();
324 }
325 for (k=2; k<resz; ++k, p(2) += d(2)) {
327 // evaluate function on next slice and construct slice interior vertices
328 dc_slice_info<T> *info_ptr_0 = slice_info_ptrs[(k-2)%3];
329 dc_slice_info<T> *info_ptr_1 = slice_info_ptrs[(k-1)%3];
330 dc_slice_info<T> *info_ptr_2 = slice_info_ptrs[k%3];
331 process_slice(info_ptr_1, info_ptr_2);
332 process_slab(info_ptr_1, info_ptr_2);
333 generate_slice_quads(info_ptr_0, info_ptr_1);
334
336 nr_vertices[k%4] = n;
337 n = nr_vertices[(k+3)%4];
338 if (n > 0)
340 // show progression
341 if (show_progress)
342 prog.step();
343 }
344 }
345};
346 }
347 }
348}
A vector with zero based index.
Definition fvec.h:26
T length() const
length of the vector L2-Norm
Definition fvec.h:249
vec< T > to_vec() const
conversion to vector type
Definition fvec.h:730
virtual vec_type evaluate_gradient(const pnt_type &p) const
interface for evaluation of the gradient of the multivariate function.
Definition mfunc.h:31
virtual T evaluate(const pnt_type &p) const =0
interface for evaluation of the multivariate function
dimension independent implementation of quadric error metrics
Definition qem.h:14
vec< T > minarg(const vec< T > &p_ref, T relative_epsilon, T max_distance=-1, T epsilon=1e-10) const
compute point that minimizes distance to qem and is inside the sphere of radius max_distance around p...
Definition qem.h:98
specialization of a multivariate function to three independent variables, which only reimplements the...
Definition mfunc.h:63
A column vector class.
Definition vec.h:28
unsigned size() const
number of elements
Definition vec.h:59
T * data()
cast into non const array
Definition vec.h:192
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
cell_info< X > cell_info_type
qem type must have dimension three
void process_slice(dc_slice_info< T > *prev_info_ptr, dc_slice_info< T > *info_ptr)
process a slice
cgv::math::fvec< X, 3 > pnt_type
points must have three components
void compute_cell_vertex(dc_slice_info< T > *info_ptr, int i, int j)
construct a new vertex on an edge
cgv::math::qem< X > qem_type
qem type must have dimension three
void generate_quad(unsigned int vi, unsigned int vj, unsigned int vk, unsigned int vl, bool reorient)
construct a quadrilateral
void process_edge_plane(const T &v_1, const T &v_2, int e, cell_info_type *C1, cell_info_type *C2, cell_info_type *C3, cell_info_type *C4)
construct plane through edge and add it to the incident qems
dual_contouring(const cgv::math::v3_func< X, T > &_func, streaming_mesh_callback_handler *_smcbh, const X &_consistency_threshold=0.01f, unsigned int _max_nr_iters=10, const X &_epsilon=1e-6f)
construct dual contouring object
cgv::math::fvec< X, 3 > vec_type
vectors must have three components
void extract(const T &_iso_value, const axis_aligned_box< X, 3 > &box, unsigned int _resx, unsigned int _resy, unsigned int _resz, bool show_progress=false)
extract iso surface and send quads to dual contouring handler
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_quad(unsigned int vi, unsigned int vj, unsigned int vk, unsigned int vl)
construct a new quad 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
information stored per cell
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