1 #ifndef VOLUME_H
2 #define VOLUME_H
3 
4 #include <QDebug>
5 #include <vector> //STD vector
6 #include <QPainter> // Draw slice
7 #include <common/interfaces.h> // Lots of meshlab stuff
8 
9 #include <vcg/complex/algorithms/create/mc_trivial_walker.h> // SimpleVolume
10 #include <vcg/complex/algorithms/create/marching_cubes.h> // Marching cube
11 #include "gridaccell.h" // Grid ray accellerator
12 #include "vase_utils.h"
13 
14 
15 namespace vcg{
16 
17 // Forward declare
18 class GridAccell;
19 
20 /**
21   * This is a trivial extension of the class vcg::SimpleVoxel, it does not only
22   * hold a field value, but also a pointer to the face responsible for that field
23   * value. If a face needs to move (perpendicular to the normal, like in a balloon)
24   * then we will be able to retrieve the necessary movement of this field by visiting
25   * the corresponding face and querying the variation at that particular location.
26   */
27 class MyVoxel{
28     public:
29         float   sfield;   // signed distance from surface
30         float   field;   // unsigned distance from surface (used for the band updating)
31         CFaceO* face;    // corresponding balloon face
32         int     status;  // status: {0: untouched, 1: in queue, 2: popped}
33         int     index;   // index of MyVoxel in current active band
34         /// Set field to zero and NULL the face pointer
MyVoxel()35         MyVoxel(){
36             sfield  = 0;
37             field  = 0;
38             face   = 0;
39             status = 0;
40             index  = 0;
41         }
42         /// Required by marching cubes
V()43         float &V(){
44             return sfield;
45         }
46 };
47  /**
48   * An implicit  volumetric class:
49   *
50   * There is a fishy relationship in between the indexing used in the volume class and the
51   * one used in the gridaccell class. I think it's related to the way in which marching cubes
52   * extracts the isosurface. While gridaccell will have the -.5/+.5 correction, the volume
53   * needs to work on integer coordinates with no post-correction. In this way the extracted
54   * isosurface is perfectly aligned with the ray-grid created by gridaccell. I would like to
55   * investigate this particular relationship but the tests show that it work... and that's
56   * all I have time for. If anything can be changed, it's within volume... gridaccell should
57   * be left alone as the 3D-DDR is highly dependent on the way in which the grid is
58   * structured.
59   *
60   */
61 class MyVolume{
62     private:
63         // Internal uniform grid representation
64         SimpleVolume<MyVoxel> grid;
65         // Size of the underlying grid (voxels)
66         Point3i sz;
67         // Padding of the volume
68         int padsize;
69         // Real dimension of a voxel
70         float delta;
71         // Volume represented by the grid
72         vcg::Box3f bbox;
73         // Pre allocated 2D matrixes for slicing
74         QPixmap  slices_2D[3];
75 
76     public:
77 
78         /// Compute volume-to-surface correspondences (stored in MyVoxel::face) in a band around the surface
79         /// up to DELTA away (in object space) inserting the volumetric indexes in the band vector.
80         /// Used by initField(CMeshO&, accell)
81         void updateSurfaceCorrespondence( CMeshO& balloon_mesh, GridAccell& gridAccell, float DELTA );
82 
83         /// Indexes of current band
84         std::vector<Point3i> band;
85 
86         // Constructors / Factories
87         /// Empty, refer to Init(...)
MyVolume()88         MyVolume(){ sz=Point3i(0,0,0); }
89         /// Initialize the volume
90         void init( int gridsize, int padsize, vcg::Box3f bbox );
91         /// Retrieves a 2D (image) slice from the volume
92         QPixmap& getSlice(int dim, int slice);
93         /// Set SEDF according to continuous function
94         void initField( const vcg::Box3f&  bbox );
95         /// Set SEDF so that MC woudl give "surface" as output
96         void initField( CMeshO& surface, GridAccell& accell );
97         /// Computes isosurface of current volume
98         void isosurface( CMeshO& balloon_mesh, float offset=0 );
99         /// Render every cube just for illustration
100         void render();
101         /// Check sample in range
checkRange(Point3i newo)102         bool checkRange( Point3i newo ){
103             assert( newo[0] >= 0 && newo[0] < size(0) );
104             assert( newo[1] >= 0 && newo[1] < size(1) );
105             assert( newo[2] >= 0 && newo[2] < size(2) );
106         }
107 
108 
109         // Getters
getDelta()110         float getDelta() const{ return delta; }
getBbox()111         vcg::Box3f getBbox() const{ return bbox; }
getSize()112         Point3i getSize() const{ return sz; }
getPadding()113         int getPadding() const{ return padsize; };
114 
115         /// Has this volume been initialized?
isInit()116         inline bool isInit(){
117             return size(0)>0 && size(1)>0 && size(2)>0;
118         }
119 
120         // Access methods (for assignments and similar)
Voxel(const Point3i off)121         MyVoxel& Voxel(const Point3i off){
122             return Voxel( off[0], off[1], off[2] );
123         }
Voxel(const int & x,const int & y,const int & z)124         MyVoxel& Voxel(const int &x,const int &y,const int &z){
125             return grid.V( x, y, z );
126         }
Val(const Point3i off)127         float &Val(const Point3i off){
128             return Val( off[0], off[1], off[2] );
129         }
Val(const int & x,const int & y,const int & z)130         float &Val(const int &x,const int &y,const int &z) {
131             return grid.Val(x,y,z);
132         }
133 
134         // Info functions
135         /// Is the offset (no rounding) within the bounds of the grid (+pad)
isIn(const Point3f & off)136         bool isIn( const Point3f& off ){
137             Point3i ofi( round(off[0]), round(off[1]), round(off[2]) );
138             return ofi[0]>=0 && ofi[0]<size(0) &&
139                    ofi[1]>=0 && ofi[1]<size(1) &&
140                    ofi[2]>=0 && ofi[2]<size(2);
141         }
size()142         const Point3i& size(){ return grid.ISize(); }
size(int dim)143         inline int size(int dim) { return sz[dim]; }
144         /// Converts a general position to a voxel index
pos2off(const Point3f & pos,Point3i & off)145         void pos2off( const Point3f& pos, Point3i& off ){
146             off[0] = pos2off( pos[0], 0 );
147             off[1] = pos2off( pos[1], 1 );
148             off[2] = pos2off( pos[2], 2 );
149         }
150         /// Converts a general position to a voxel index
pos2off(float p,int dim)151         int pos2off( float p, int dim ){
152             int v = round( (p-bbox.min[dim])/delta + padsize);
153             return myclamp( v, 0, size(dim));
154         }
155         /// Converts a volumetric offset in a 3D space coordinate
off2pos(const Point3i & off,Point3f & pos)156         void off2pos( const Point3i& off, Point3f& pos ){
157             pos[0] = off2pos( off[0], 0 );
158             pos[1] = off2pos( off[1], 1 );
159             pos[2] = off2pos( off[2], 2 );
160         }
161         /// Converts a volumetric offset in a 3D space coordinate
off2pos(int i,int j,int k,Point3f & pos)162         void off2pos( int i, int j, int k, Point3f& pos ){
163             pos[0] = off2pos( i, 0 );
164             pos[1] = off2pos( j, 1 );
165             pos[2] = off2pos( k, 2 );
166         }
167         /// Converts an offset on grid in object space coordinate
off2pos(int i,int dim)168         float off2pos( int i, int dim ){
169             return (i-padsize)*delta + bbox.min[dim];
170         }
171         /// Returns the bounding box
get_bbox()172         const vcg::Box3f& get_bbox(){
173             return this->bbox;
174         }
175         /// Returns a string representing the content of the volume at
176         /// a particular slice along dimension dim
177         QString toString(int dim=2, int a=5);
178 };
179 
180 } // end namespace vcg
181 #endif // VOLUME_H
182