180 lines
6.1 KiB
C
180 lines
6.1 KiB
C
|
#pragma once
|
||
|
|
||
|
#include <vector>
|
||
|
#include <deque>
|
||
|
#include <cgv/utils/progression.h>
|
||
|
#include <cgv/math/qem.h>
|
||
|
#include <cgv/math/mfunc.h>
|
||
|
#include <cgv/media/axis_aligned_box.h>
|
||
|
#include "streaming_mesh.h"
|
||
|
|
||
|
namespace cgv {
|
||
|
namespace media {
|
||
|
namespace mesh {
|
||
|
|
||
|
template <typename T>
|
||
|
struct greater_equal
|
||
|
{
|
||
|
T reference_value;
|
||
|
greater_equal(const T& _value) : reference_value(_value) {}
|
||
|
bool operator () (const T& _value) const { return _value >= reference_value; }
|
||
|
};
|
||
|
|
||
|
template <typename T>
|
||
|
struct equal
|
||
|
{
|
||
|
T reference_value;
|
||
|
equal(const T& _value) : reference_value(_value) {}
|
||
|
bool operator () (const T& _value) const { return _value == reference_value; }
|
||
|
};
|
||
|
|
||
|
|
||
|
/** data structure for the information that is cached per volume slice bz the cuberille algorithm */
|
||
|
template <typename T, class P>
|
||
|
struct c_slice_info
|
||
|
{
|
||
|
unsigned resx, resy;
|
||
|
unsigned nr_vertices;
|
||
|
std::vector<bool> flags;
|
||
|
std::vector<int> indices;
|
||
|
///
|
||
|
c_slice_info(unsigned int _resx, unsigned int _resy) : resx(_resx), resy(_resy) {
|
||
|
unsigned int n = resx*resy;
|
||
|
flags.resize(n, false);
|
||
|
indices.resize(n, -1);
|
||
|
nr_vertices = 0;
|
||
|
}
|
||
|
///
|
||
|
void init() {
|
||
|
std::fill(flags.begin(), flags.end(), false);
|
||
|
std::fill(indices.begin(), indices.end(), -1);
|
||
|
nr_vertices = 0;
|
||
|
}
|
||
|
int linear_index(int x, int y) const { return y*resx + x; }
|
||
|
///
|
||
|
bool flag(int x, int y) const { return (x>=0) && (y>=0) && flags[linear_index(x, y)]; }
|
||
|
///
|
||
|
void set_flag(int x, int y, bool flag) { flags[linear_index(x, y)] = flag; }
|
||
|
///
|
||
|
const int& index(int x, int y) const { return indices[linear_index(x, y)]; }
|
||
|
void set_index(int x, int y, int idx) {
|
||
|
indices[linear_index(x,y)] = idx;
|
||
|
++nr_vertices;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/// class used to perform the marching cubes algorithm
|
||
|
template <typename X, typename T, class P>
|
||
|
class cuberille : public streaming_mesh<X>
|
||
|
{
|
||
|
public:
|
||
|
typedef streaming_mesh<X> base_type;
|
||
|
/// points must have three components
|
||
|
typedef cgv::math::fvec<X,3> pnt_type;
|
||
|
/// vectors must have three components
|
||
|
typedef cgv::math::fvec<X,3> vec_type;
|
||
|
private:
|
||
|
pnt_type p, minp;
|
||
|
unsigned int resx, resy, resz;
|
||
|
vec_type d;
|
||
|
const P& pred;
|
||
|
protected:
|
||
|
const cgv::math::v3_func<X,T>& func;
|
||
|
public:
|
||
|
/// construct dual contouring object
|
||
|
cuberille(const cgv::math::v3_func<X,T>& _func,
|
||
|
streaming_mesh_callback_handler* _smcbh,
|
||
|
const P& _pred) :
|
||
|
func(_func), pred(_pred)
|
||
|
{
|
||
|
base_type::set_callback_handler(_smcbh);
|
||
|
}
|
||
|
/// construct a quadrilateral
|
||
|
void generate_edge_quad(int vi, int vj, int vk, int vl, bool reorient)
|
||
|
{
|
||
|
if (reorient)
|
||
|
base_type::new_quad(vi, vl, vk, vj);
|
||
|
else
|
||
|
base_type::new_quad(vi, vj, vk, vl);
|
||
|
}
|
||
|
/// generate all vertices needed in the given slice I[0] with I[1] being the previous slice
|
||
|
void process_slice(c_slice_info<T, P>* I[2])
|
||
|
{
|
||
|
// init slice info
|
||
|
I[0]->init();
|
||
|
// iterate voxels of slice to create slice vertices
|
||
|
unsigned i, j;
|
||
|
for (j = 0, p(1) = minp(1); j <= resy; ++j, p(1) += d(1)) {
|
||
|
for (i = 0, p(0) = minp(0); i <= resx; ++i, p(0) += d(0)) {
|
||
|
// set voxel flag
|
||
|
I[0]->set_flag(i, j, i < resx && j < resy && pred(func.evaluate(p.to_vec())));
|
||
|
// and check whether assigned vertex is needed
|
||
|
bool need_vertex = false;
|
||
|
need_vertex = need_vertex || (I[0]->flag(i, j) != I[1]->flag(i, j)); // z(x0,y0)
|
||
|
need_vertex = need_vertex || (I[0]->flag(i, j) != I[0]->flag(i, j - 1)); // y(x0,z0)
|
||
|
need_vertex = need_vertex || (I[1]->flag(i, j) != I[1]->flag(i, j - 1)); // y(x0,z1)
|
||
|
need_vertex = need_vertex || (I[0]->flag(i, j) != I[0]->flag(i - 1, j)); // x(y0,z0)
|
||
|
need_vertex = need_vertex || (I[1]->flag(i, j) != I[1]->flag(i - 1, j)); // x(y0,z1)
|
||
|
if (i > 0) {
|
||
|
need_vertex = need_vertex || (I[0]->flag(i - 1, j) != I[1]->flag(i - 1, j)); // z(x1,y0)
|
||
|
need_vertex = need_vertex || (I[0]->flag(i - 1, j) != I[0]->flag(i - 1, j - 1)); // y(x1,z0)
|
||
|
need_vertex = need_vertex || (I[1]->flag(i - 1, j) != I[1]->flag(i - 1, j - 1)); // y(x1,z1)
|
||
|
if (j > 0) {
|
||
|
need_vertex = need_vertex || (I[0]->flag(i - 1, j - 1) != I[1]->flag(i - 1, j - 1)); // z(x1,y1)
|
||
|
}
|
||
|
}
|
||
|
if (j > 0) {
|
||
|
need_vertex = need_vertex || (I[0]->flag(i, j - 1) != I[1]->flag(i - 1, j)); // z(x0,y1)
|
||
|
need_vertex = need_vertex || (I[0]->flag(i, j - 1) != I[0]->flag(i - 1, j - 1)); // x(y1,z0)
|
||
|
need_vertex = need_vertex || (I[1]->flag(i, j - 1) != I[1]->flag(i - 1, j - 1)); // x(y1,z1)
|
||
|
}
|
||
|
// create vertex if necessary
|
||
|
if (need_vertex)
|
||
|
I[0]->set_index(i, j, new_vertex(p - T(0.5)*d));
|
||
|
}
|
||
|
}
|
||
|
// iterate voxels again to create edge quads
|
||
|
for (j = 0, p(1) = minp(1); j <= resy; ++j, p(1) += d(1)) {
|
||
|
for (i = 0, p(0) = minp(0); i <= resx; ++i, p(0) += d(0)) {
|
||
|
if ((j < resy) && (I[1]->flag(i - 1, j) != I[1]->flag(i, j)))
|
||
|
generate_edge_quad(I[1]->index(i, j), I[1]->index(i, j + 1), I[0]->index(i, j + 1), I[0]->index(i, j), I[1]->flag(i, j));
|
||
|
if ((i < resx) && (I[1]->flag(i, j - 1) != I[1]->flag(i, j)))
|
||
|
generate_edge_quad(I[1]->index(i, j), I[0]->index(i, j), I[0]->index(i + 1, j), I[1]->index(i + 1, j), I[1]->flag(i, j));
|
||
|
if ((i < resx) && (j < resy) && (I[1]->flag(i, j) != I[0]->flag(i, j)))
|
||
|
generate_edge_quad(I[0]->index(i, j), I[0]->index(i + 1, j), I[0]->index(i + 1, j + 1), I[0]->index(i, j + 1), I[0]->flag(i, j));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/// extract iso surface and send quads to streaming mesh handler
|
||
|
void extract(const axis_aligned_box<X,3>& box,
|
||
|
unsigned int _resx, unsigned int _resy, unsigned int _resz,
|
||
|
bool show_progress = false)
|
||
|
{
|
||
|
// prepare private members
|
||
|
resx = _resx; resy = _resy; resz = _resz;
|
||
|
minp = p = box.get_min_pnt();
|
||
|
d = box.get_extent();
|
||
|
d(0) /= (resx-1); d(1) /= (resy-1); d(2) /= (resz-1);
|
||
|
|
||
|
// prepare progression
|
||
|
cgv::utils::progression prog;
|
||
|
if (show_progress)
|
||
|
prog.init("extraction", resz+1, 10);
|
||
|
|
||
|
// construct three slice infos
|
||
|
c_slice_info<T, P> slice_info_1(resx+1,resy+1), slice_info_2(resx+1,resy+1);
|
||
|
c_slice_info<T, P> *I[2] = { &slice_info_1, &slice_info_2 };
|
||
|
for (unsigned k=0; k<=resz; ++k, p(2) += d(2)) {
|
||
|
process_slice(I);
|
||
|
base_type::drop_vertices(I[1]->nr_vertices);
|
||
|
// show progression
|
||
|
if (show_progress)
|
||
|
prog.step();
|
||
|
std::swap(I[0], I[1]);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
}
|