Repository: dgpdec/course Branch: master Commit: 9f29f075989e Files: 393 Total size: 59.4 MB Directory structure: gitextract_rojb_la7/ ├── .gitignore ├── BaseCode/ │ ├── .viewer_state.txt │ ├── Makefile │ ├── include/ │ │ ├── Application.h │ │ ├── Camera.h │ │ ├── Complex.h │ │ ├── DenseMatrix.h │ │ ├── DiscreteExteriorCalculus.h │ │ ├── Edge.h │ │ ├── Face.h │ │ ├── HalfEdge.h │ │ ├── Image.h │ │ ├── LinearContext.h │ │ ├── LinearEquation.h │ │ ├── LinearPolynomial.h │ │ ├── LinearSystem.h │ │ ├── Mesh.h │ │ ├── MeshIO.h │ │ ├── Quaternion.h │ │ ├── Real.h │ │ ├── Shader.h │ │ ├── SparseMatrix.h │ │ ├── Types.h │ │ ├── Utility.h │ │ ├── Variable.h │ │ ├── Vector.h │ │ ├── Vertex.h │ │ └── Viewer.h │ ├── obj/ │ │ └── .empty │ ├── shaders/ │ │ ├── fragment.glsl │ │ └── vertex.glsl │ └── src/ │ ├── Camera.cpp │ ├── Complex.cpp │ ├── DenseMatrix.cpp │ ├── DenseMatrix.inl │ ├── DiscreteExteriorCalculus.inl │ ├── Edge.cpp │ ├── Face.cpp │ ├── HalfEdge.cpp │ ├── Image.cpp │ ├── LinearContext.cpp │ ├── LinearEquation.cpp │ ├── LinearPolynomial.cpp │ ├── LinearSystem.cpp │ ├── Mesh.cpp │ ├── MeshIO.cpp │ ├── Quaternion.cpp │ ├── Real.cpp │ ├── Shader.cpp │ ├── SparseMatrix.cpp │ ├── SparseMatrix.inl │ ├── Variable.cpp │ ├── Vector.cpp │ ├── Vertex.cpp │ ├── Viewer.cpp │ └── main.cpp ├── Connection/ │ ├── Makefile │ ├── include/ │ │ ├── Application.h │ │ ├── Camera.h │ │ ├── Complex.h │ │ ├── DenseMatrix.h │ │ ├── Direction.h │ │ ├── DiscreteExteriorCalculus.h │ │ ├── Edge.h │ │ ├── Face.h │ │ ├── HalfEdge.h │ │ ├── HarmonicBases.h │ │ ├── Image.h │ │ ├── LinearContext.h │ │ ├── LinearEquation.h │ │ ├── LinearPolynomial.h │ │ ├── LinearSystem.h │ │ ├── Mesh.h │ │ ├── MeshIO.h │ │ ├── Quaternion.h │ │ ├── Real.h │ │ ├── Shader.h │ │ ├── SparseMatrix.h │ │ ├── TreeCotree.h │ │ ├── Types.h │ │ ├── Utility.h │ │ ├── Variable.h │ │ ├── Vector.h │ │ ├── Vertex.h │ │ └── Viewer.h │ ├── obj/ │ │ └── .empty │ ├── shaders/ │ │ ├── fragment.glsl │ │ └── vertex.glsl │ └── src/ │ ├── Camera.cpp │ ├── Complex.cpp │ ├── DenseMatrix.cpp │ ├── DenseMatrix.inl │ ├── DiscreteExteriorCalculus.inl │ ├── Edge.cpp │ ├── Face.cpp │ ├── HalfEdge.cpp │ ├── Image.cpp │ ├── LinearContext.cpp │ ├── LinearEquation.cpp │ ├── LinearPolynomial.cpp │ ├── LinearSystem.cpp │ ├── Mesh.cpp │ ├── MeshIO.cpp │ ├── Quaternion.cpp │ ├── Real.cpp │ ├── Shader.cpp │ ├── SparseMatrix.cpp │ ├── SparseMatrix.inl │ ├── Variable.cpp │ ├── Vector.cpp │ ├── Vertex.cpp │ ├── Viewer.cpp │ └── main.cpp ├── Elasticity/ │ ├── .viewer_state.txt │ ├── Makefile │ ├── include/ │ │ ├── .Elasticity.h.swo │ │ ├── Application.h │ │ ├── Camera.h │ │ ├── Complex.h │ │ ├── DenseMatrix.h │ │ ├── DiscreteExteriorCalculus.h │ │ ├── Edge.h │ │ ├── Face.h │ │ ├── HalfEdge.h │ │ ├── Image.h │ │ ├── LinearContext.h │ │ ├── LinearEquation.h │ │ ├── LinearPolynomial.h │ │ ├── LinearSystem.h │ │ ├── Mesh.h │ │ ├── MeshIO.h │ │ ├── PolarDecomposition2x2.h │ │ ├── Quaternion.h │ │ ├── Real.h │ │ ├── Shader.h │ │ ├── SparseMatrix.h │ │ ├── Types.h │ │ ├── Utility.h │ │ ├── Variable.h │ │ ├── Vector.h │ │ ├── Vertex.h │ │ └── Viewer.h │ ├── obj/ │ │ └── .empty │ ├── shaders/ │ │ ├── fragment.glsl │ │ └── vertex.glsl │ └── src/ │ ├── Camera.cpp │ ├── Complex.cpp │ ├── DenseMatrix.cpp │ ├── DenseMatrix.inl │ ├── DiscreteExteriorCalculus.inl │ ├── Edge.cpp │ ├── Face.cpp │ ├── HalfEdge.cpp │ ├── Image.cpp │ ├── LinearContext.cpp │ ├── LinearEquation.cpp │ ├── LinearPolynomial.cpp │ ├── LinearSystem.cpp │ ├── Mesh.cpp │ ├── MeshIO.cpp │ ├── Quaternion.cpp │ ├── Real.cpp │ ├── Shader.cpp │ ├── SparseMatrix.cpp │ ├── SparseMatrix.inl │ ├── Variable.cpp │ ├── Vector.cpp │ ├── Vertex.cpp │ ├── Viewer.cpp │ └── main.cpp ├── Fairing/ │ ├── .viewer_state.txt │ ├── Makefile │ ├── include/ │ │ ├── Application.h │ │ ├── Camera.h │ │ ├── Complex.h │ │ ├── DenseMatrix.h │ │ ├── DiscreteExteriorCalculus.h │ │ ├── Edge.h │ │ ├── Face.h │ │ ├── HalfEdge.h │ │ ├── Image.h │ │ ├── LinearContext.h │ │ ├── LinearEquation.h │ │ ├── LinearPolynomial.h │ │ ├── LinearSystem.h │ │ ├── Mesh.h │ │ ├── MeshIO.h │ │ ├── Quaternion.h │ │ ├── Real.h │ │ ├── Shader.h │ │ ├── SparseMatrix.h │ │ ├── Types.h │ │ ├── Utility.h │ │ ├── Variable.h │ │ ├── Vector.h │ │ ├── Vertex.h │ │ └── Viewer.h │ ├── obj/ │ │ └── .empty │ ├── shaders/ │ │ ├── fragment.glsl │ │ └── vertex.glsl │ └── src/ │ ├── Camera.cpp │ ├── Complex.cpp │ ├── DenseMatrix.cpp │ ├── DenseMatrix.inl │ ├── DiscreteExteriorCalculus.inl │ ├── Edge.cpp │ ├── Face.cpp │ ├── HalfEdge.cpp │ ├── Image.cpp │ ├── LinearContext.cpp │ ├── LinearEquation.cpp │ ├── LinearPolynomial.cpp │ ├── LinearSystem.cpp │ ├── Mesh.cpp │ ├── MeshIO.cpp │ ├── Quaternion.cpp │ ├── Real.cpp │ ├── Shader.cpp │ ├── SparseMatrix.cpp │ ├── SparseMatrix.inl │ ├── Variable.cpp │ ├── Vector.cpp │ ├── Vertex.cpp │ ├── Viewer.cpp │ └── main.cpp ├── Flatten/ │ ├── .viewer_state.txt │ ├── include/ │ │ ├── Application.h │ │ ├── Camera.h │ │ ├── Complex.h │ │ ├── DenseMatrix.h │ │ ├── DiscreteExteriorCalculus.h │ │ ├── Edge.h │ │ ├── Face.h │ │ ├── HalfEdge.h │ │ ├── Image.h │ │ ├── LinearContext.h │ │ ├── LinearEquation.h │ │ ├── LinearPolynomial.h │ │ ├── LinearSystem.h │ │ ├── Mesh.h │ │ ├── MeshIO.h │ │ ├── Quaternion.h │ │ ├── Real.h │ │ ├── Shader.h │ │ ├── SparseMatrix.h │ │ ├── Types.h │ │ ├── Utility.h │ │ ├── Variable.h │ │ ├── Vector.h │ │ ├── Vertex.h │ │ └── Viewer.h │ ├── obj/ │ │ └── .empty │ ├── shaders/ │ │ ├── fragment.glsl │ │ └── vertex.glsl │ └── src/ │ ├── Camera.cpp │ ├── Complex.cpp │ ├── DenseMatrix.cpp │ ├── DenseMatrix.inl │ ├── DiscreteExteriorCalculus.inl │ ├── Edge.cpp │ ├── Face.cpp │ ├── HalfEdge.cpp │ ├── Image.cpp │ ├── LinearContext.cpp │ ├── LinearEquation.cpp │ ├── LinearPolynomial.cpp │ ├── LinearSystem.cpp │ ├── Mesh.cpp │ ├── MeshIO.cpp │ ├── Quaternion.cpp │ ├── Real.cpp │ ├── Shader.cpp │ ├── SparseMatrix.cpp │ ├── SparseMatrix.inl │ ├── Variable.cpp │ ├── Vector.cpp │ ├── Vertex.cpp │ ├── Viewer.cpp │ ├── main.cpp │ └── qcError.cpp ├── Geodesics/ │ ├── .viewer_state.txt │ ├── Makefile │ ├── include/ │ │ ├── Application.h │ │ ├── Camera.h │ │ ├── Complex.h │ │ ├── DenseMatrix.h │ │ ├── DiscreteExteriorCalculus.h │ │ ├── Edge.h │ │ ├── Face.h │ │ ├── HalfEdge.h │ │ ├── Image.h │ │ ├── LinearContext.h │ │ ├── LinearEquation.h │ │ ├── LinearPolynomial.h │ │ ├── LinearSystem.h │ │ ├── Mesh.h │ │ ├── MeshIO.h │ │ ├── Quaternion.h │ │ ├── Real.h │ │ ├── Shader.h │ │ ├── SparseMatrix.h │ │ ├── Types.h │ │ ├── Utility.h │ │ ├── Variable.h │ │ ├── Vector.h │ │ ├── Vertex.h │ │ └── Viewer.h │ ├── obj/ │ │ └── .empty │ ├── shaders/ │ │ ├── fragment.glsl │ │ └── vertex.glsl │ └── src/ │ ├── Camera.cpp │ ├── Complex.cpp │ ├── DenseMatrix.cpp │ ├── DenseMatrix.inl │ ├── DiscreteExteriorCalculus.inl │ ├── Edge.cpp │ ├── Face.cpp │ ├── HalfEdge.cpp │ ├── Image.cpp │ ├── LinearContext.cpp │ ├── LinearEquation.cpp │ ├── LinearPolynomial.cpp │ ├── LinearSystem.cpp │ ├── Mesh.cpp │ ├── MeshIO.cpp │ ├── Quaternion.cpp │ ├── Real.cpp │ ├── Shader.cpp │ ├── SparseMatrix.cpp │ ├── SparseMatrix.inl │ ├── Variable.cpp │ ├── Vector.cpp │ ├── Vertex.cpp │ ├── Viewer.cpp │ └── main.cpp ├── HOWTO ├── Hot2/ │ ├── .viewer_state.txt │ ├── Makefile │ ├── include/ │ │ ├── Application.h │ │ ├── Camera.h │ │ ├── Complex.h │ │ ├── DenseMatrix.h │ │ ├── DiscreteExteriorCalculus.h │ │ ├── Edge.h │ │ ├── Face.h │ │ ├── HalfEdge.h │ │ ├── Image.h │ │ ├── LinearContext.h │ │ ├── LinearEquation.h │ │ ├── LinearPolynomial.h │ │ ├── LinearSystem.h │ │ ├── Mesh.h │ │ ├── MeshIO.h │ │ ├── Quaternion.h │ │ ├── Real.h │ │ ├── Shader.h │ │ ├── SparseMatrix.h │ │ ├── Types.h │ │ ├── Utility.h │ │ ├── Variable.h │ │ ├── Vector.h │ │ ├── Vertex.h │ │ └── Viewer.h │ ├── obj/ │ │ └── .empty │ ├── shaders/ │ │ ├── fragment.glsl │ │ └── vertex.glsl │ └── src/ │ ├── Camera.cpp │ ├── Complex.cpp │ ├── DenseMatrix.cpp │ ├── DenseMatrix.inl │ ├── DiscreteExteriorCalculus.inl │ ├── Edge.cpp │ ├── Face.cpp │ ├── HalfEdge.cpp │ ├── Image.cpp │ ├── LinearContext.cpp │ ├── LinearEquation.cpp │ ├── LinearPolynomial.cpp │ ├── LinearSystem.cpp │ ├── Mesh.cpp │ ├── MeshIO.cpp │ ├── Quaternion.cpp │ ├── Real.cpp │ ├── Shader.cpp │ ├── SparseMatrix.cpp │ ├── SparseMatrix.inl │ ├── Variable.cpp │ ├── Vector.cpp │ ├── Vertex.cpp │ ├── Viewer.cpp │ └── main.cpp └── Slides/ ├── apps-siggraph2013.key └── theory-siggraph2013.key ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store *.o *.swp Flatten/Makefile Flatten/flatten ================================================ FILE: BaseCode/.viewer_state.txt ================================================ 0.126663 -0.0243652 -0.989174 0.0699843 815 767 ================================================ FILE: BaseCode/Makefile ================================================ ########################################################################################## # Specify library locations here (add or remove "#" marks to comment/uncomment lines for your platform) # Mac OS X DDG_INCLUDE_PATH = DDG_LIBRARY_PATH = DDG_BLAS_LIBS = -framework Accelerate DDG_SUITESPARSE_LIBS = -lspqr -lumfpack -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -ltbb -lm -lsuitesparseconfig DDG_OPENGL_LIBS = -framework OpenGL -framework GLUT # # Linux # DDG_INCLUDE_PATH = # DDG_LIBRARY_PATH = # DDG_BLAS_LIBS = -llapack -lblas -lgfortran # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut -lGL -lGLU -lX11 # # Windows / Cygwin # DDG_INCLUDE_PATH = -I/usr/include/opengl -I/usr/include/suitesparse # DDG_LIBRARY_PATH = -L/usr/lib/w32api -L/usr/lib/suitesparse # DDG_BLAS_LIBS = -llapack -lblas # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut32 -lglu32 -lopengl32 ######################################################################################## TARGET = ddg CC = g++ LD = g++ CFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_INCLUDE_PATH) -I./include -I./src LFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_LIBRARY_PATH) LIBS = $(DDG_OPENGL_LIBS) $(DDG_SUITESPARSE_LIBS) $(DDG_BLAS_LIBS) ######################################################################################## ## !! Do not edit below this line HEADERS := $(wildcard include/*.h) SOURCES := $(wildcard src/*.cpp) OBJECTS := $(addprefix obj/,$(notdir $(SOURCES:.cpp=.o))) all: $(TARGET) $(TARGET): $(OBJECTS) $(LD) $(OBJECTS) -o $(TARGET) $(CFLAGS) $(LFLAGS) $(LIBS) obj/%.o: src/%.cpp ${HEADERS} $(CC) -c $< -o $@ $(CFLAGS) clean: rm -f $(OBJECTS) rm -f $(TARGET) rm -f $(TARGET).exe ================================================ FILE: BaseCode/include/Application.h ================================================ #ifndef DDG_APPLICATION_H #define DDG_APPLICATION_H namespace DDG { class Application { // // TODO: add your code here!!! // }; } #endif ================================================ FILE: BaseCode/include/Camera.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Camera.h // ----------------------------------------------------------------------------- // // Camera is used by Viewer to keep track of the view state; it also // handles mouse input related to camera manipulation. // #ifndef DDG_CAMERA_H #define DDG_CAMERA_H #include "Quaternion.h" #ifdef __CYGWIN__ #define GLUT_DISABLE_ATEXIT_HACK #include #include #include #include #else #include #endif namespace DDG { class Camera { public: Camera( void ); // constructor Quaternion clickToSphere( int x, int y ); // projects a mous click onto the unit sphere void setView( void ) const; // applies the camera transformation to the OpenGL modelview stack void mouse( int button, int state, int x, int y ); // handles mouse clicks void motion( int x, int y ); // handles mouse drags void idle( void ); // handles camera momentum void zoomIn( void ); // moves viewer toward object void zoomOut( void ); // moves viewer away from object Quaternion currentRotation( void ) const; // returns the rotation corresponding to the current mouse state Quaternion pClick; // mouse coordinates of current click Quaternion pDrag; // mouse coordinates of current drag Quaternion pLast; // mouse coordinates of previous drag Quaternion rLast; // previous camera rotation Quaternion momentum; // camera momentum int tLast; // time of previous drag double zoom, vZoom; // zoom and zoom velocity }; } #endif ================================================ FILE: BaseCode/include/Complex.h ================================================ #ifndef DDG_COMPLEX_H #define DDG_COMPLEX_H #include namespace DDG { class Complex { public: Complex( double a=0., double b=0. ); // constructs number a+bi void operator+=( const Complex& z ); // add z void operator-=( const Complex& z ); // subtract z void operator*=( const Complex& z ); // Complex multiply by z void operator*=( double r ); // scalar multiply by r void operator/=( double r ); // scalar divide by r void operator/=( const Complex& z ); // complex divide by r Complex operator-( void ) const; // returns the additive inverse Complex conj( void ) const; // returns Complex conjugate Complex inv( void ) const; // returns inverse double arg( void ) const; // returns argument double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Complex unit( void ) const; // returns complex number with unit norm and same modulus Complex exponential( void ) const; // complex exponentiation double re; // real part double im; // imaginary part }; Complex operator+( const Complex& z1, const Complex& z2 ); // binary addition Complex operator-( const Complex& z1, const Complex& z2 ); // binary subtraction Complex operator*( const Complex& z1, const Complex& z2 ); // binary Complex multiplication Complex operator*( const Complex& z, double r ); // right scalar multiplication Complex operator*( double r, const Complex& z ); // left scalar multiplication Complex operator/( const Complex& z, double r ); // scalar division Complex operator/( const Complex& z1, const Complex& z2 ); // complex division double dot( const Complex& z1, const Complex& z2 ); // inner product double cross( const Complex& z1, const Complex& z2 ); // cross product std::ostream& operator<<( std::ostream& os, const Complex& o ); // prints components } #endif ================================================ FILE: BaseCode/include/DenseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DenseMatrix.h // ----------------------------------------------------------------------------- // // DenseMatrix represents an m by n (real or complex) matrix where every // entry -- including zero-valued entries -- is stored explicitly. This // class is most commonly used to represent dense vectors in sparse linear // systems (i.e., the right hand side and the solution vector). // // A real or complex matrix is allocated via // // DenseMatrix A( m, n ); // DenseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // DenseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a DenseMatrix returns a cholmod_dense* // which can be used by routines in SuiteSparse. For basic operations, however, // you should not need to access this pointer explicitly -- see the solve() // method in SparseMatrix.h. // #ifndef DDG_DENSEMATRIX_H #define DDG_DENSEMATRIX_H #include #include "Types.h" #include namespace DDG { enum NormType { lInfinity, lOne, lTwo }; template class DenseMatrix { public: DenseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix (specifying just m yields a column vector) DenseMatrix( const DenseMatrix& A ); // copy constructor const DenseMatrix& operator=( const DenseMatrix& B ); // copies B ~DenseMatrix( void ); // destructor SparseMatrix sparse( void ); // converts to a sparse matrix int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val = 0. ); // sets all elements to val double norm( NormType type = lInfinity ) const; // returns the maximum magnitude of any entry T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element of the matrix (uses 0-based indexing) T& operator()( int index ); T operator()( int index ) const; // access the specified element of a vector (uses 0-based indexing) DenseMatrix transpose( void ) const; // returns the transpose of this matrix DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c DenseMatrix operator+( const DenseMatrix& B ) const; // returns sum of this matrix with B void operator+=( const DenseMatrix& B ); // adds B to this matrix DenseMatrix operator-( const DenseMatrix& B ) const; // returns difference of this matrix with B void operator-=( const DenseMatrix& B ); // subtracts B from this matrix DenseMatrix operator-( void ) const; // returns additive inverse of this matrix cholmod_dense* to_cholmod( void ); // returns pointer to copy of matrix in CHOLMOD format const DenseMatrix& operator=( cholmod_dense* B ); // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B void normalize( void ); // divides by Frobenius norm T sum( void ) const; // returns the sum of all entries void removeMean( void ); // removes the mean void randomize( void ); // replaces entries with uniformly distributed random real numbers in the interval [-1,1] protected: int m, n; std::vector data; cholmod_dense* cData; }; template DenseMatrix operator*( const DenseMatrix& A, const T& c ); // right scalar multiplication template DenseMatrix operator*( const T& c, const DenseMatrix& A ); // left scalar multiplication template DenseMatrix operator/( const DenseMatrix& A, const T& c ); // scalar division template T dot( const DenseMatrix& x, const DenseMatrix& y ); // returns Euclidean inner product of x and y template std::ostream& operator << (std::ostream& os, const DenseMatrix& o); // prints entries template T inner( const DenseMatrix& x, const DenseMatrix& y ); // standard inner product template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ); // inner product with respect to a diagonal inner // product B represented as a dense vector } #include "DenseMatrix.inl" #endif ================================================ FILE: BaseCode/include/DiscreteExteriorCalculus.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DiscreteExteriorCalculus.h // ----------------------------------------------------------------------------- // // Static methods for building the fundamental discrete operators (exterior // derivative, Hodge star) for 0-, 1-, and 2-forms on a surface mesh. Methods // are templated on entry type, i.e., one can build either real- or complex- // matrices using the types DDG::Real and DDG::Complex, respectively. For // instance, to build the usual Laplacian on functions, one could write // // Mesh mesh; // SparseMatrix d0, star0, star1, Delta; // // ExteriorDerivative0Form::build( mesh, d0 ); // HodgeStar0Form::build( mesh, star0 ); // HodgeStar1Form::build( mesh, star1 ); // Delta = star0.inverse() * d0.transpose() * star1 * d0; // #ifndef DDG_DISCRETEEXTERIORCALCULUS_H #define DDG_DISCRETEEXTERIORCALCULUS_H #include "Mesh.h" #include "SparseMatrix.h" namespace DDG { template< class T > struct HodgeStar0Form { static void build( const Mesh& mesh, SparseMatrix& star0 ); }; template< class T > struct HodgeStar1Form { static void build( const Mesh& mesh, SparseMatrix& star1 ); }; template< class T > struct HodgeStar2Form { static void build( const Mesh& mesh, SparseMatrix& star2 ); }; template< class T > struct ExteriorDerivative0Form { static void build( const Mesh& mesh, SparseMatrix& d0 ); }; template< class T > struct ExteriorDerivative1Form { static void build( const Mesh& mesh, SparseMatrix& d1 ); }; } #include "DiscreteExteriorCalculus.inl" #endif ================================================ FILE: BaseCode/include/Edge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Edge.h // ----------------------------------------------------------------------------- // // Edge stores attributes associated with a mesh edge. The iterator he points // to one of its two associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_EDGE_H #define DDG_EDGE_H #include "Types.h" namespace DDG { class Edge { public: HalfEdgeIter he; // points to one of the two halfedges associated with this edge int index; // unique integer ID in the range 0, ..., nEdges-1 Edge() : index(0) { } }; } #endif ================================================ FILE: BaseCode/include/Face.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Face.h // ----------------------------------------------------------------------------- // // Face stores attributes associated with a mesh edge. The iterator he points // to one of its associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_FACE_H #define DDG_FACE_H #include "Types.h" namespace DDG { class Face { public: HalfEdgeIter he; // points to one of the halfedges associated with this face int index; // unique integer ID in the range 0, ..., nFaces-1 Face() : index(0) { } bool isBoundary( void ) const; // returns true if this face corresponds to a // boundary loop; false otherwise double area( void ) const; // returns the triangle area Vector normal( void ) const; // returns the unit normal associated with this face; normal // orientation is determined by the circulation order of halfedges Vector circumcenter( void ) const; // returns triangle circumcenter Vector barycenter( void ) const; // returns triangle barycenter }; } #endif ================================================ FILE: BaseCode/include/HalfEdge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- HalfEdge.h // ----------------------------------------------------------------------------- // // HalfEdge is used to define mesh connectivity. (See the documentation for a // more in-depth discussion of the halfedge data structure.) // #ifndef DDG_HALFEDGE_H #define DDG_HALFEDGE_H #include "Vector.h" #include "Types.h" namespace DDG { class HalfEdge { public: HalfEdgeIter next; // points to the next halfedge around the current face HalfEdgeIter flip; // points to the other halfedge associated with this edge VertexIter vertex; // points to the vertex at the "tail" of this halfedge EdgeIter edge; // points to the edge associated with this halfedge FaceIter face; // points to the face containing this halfedge bool onBoundary; // true if this halfedge is contained in a boundary // loop; false otherwise Vector texcoord; // texture coordinates associated with the triangle corner at the // "tail" of this halfedge double cotan( void ) const; // returns the cotangent of the angle opposing this edge Vector rotatedEdge( void ) const; // returns oriented edge vector rotated by PI/2 around face normal // if onBoundary, then return nil }; } #endif ================================================ FILE: BaseCode/include/Image.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Image.h // ----------------------------------------------------------------------------- // // Image represents a color bitmap image. A simple example might look like // // Image im; // im.read( "input.tga" ); // // modify image data via im(x,y) = ...; // im.write( "output.tga" ); // #ifndef DDG_IMAGE_H #define DDG_IMAGE_H #include #include namespace DDG { class Image { public: Image( int width = 0, int height = 0 ); // constructs image with specified width and height float& operator()( int x, int y ); const float& operator()( int x, int y ) const; // accesses pixel (x,y) float sample( float x, float y ) const; // samples image at (x,y) using bilinear filtering int width( void ) const; int height( void ) const; // returns image dimensions void read( const char* filename ); // loads an image file in Truevision TGA format // (must be RGB image with 24 or 32 bits per pixel) void write( const char* filename ) const; // writes an image file in Truevision TGA format // (RGB image with 24 bits per pixel) protected: void clamp( int& x, int& y ) const; // clamps coordinates to range [0,w-1] x [0,h-1] int w, h; // width and height std::vector pixels; // interleaved RGBA pixel data in range [0-1] }; } #endif ================================================ FILE: BaseCode/include/LinearContext.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearContext.h // ----------------------------------------------------------------------------- // // LinearContext is the global solver context needed to interface with the // SuiteSparse library. It is essentially a wrapper around cholmod_common. A // single static instance of LinearContext is declared in LinearContext.cpp and // is shared by all instances of DenseMatrix, SparseMatrix, and LinearSystem. // In other words, you shouldn't have to instantiate LinearContext yourself // unless you're doing something really fancy! // #ifndef DDG_LINEARSOLVERCONTEXT #define DDG_LINEARSOLVERCONTEXT #include namespace DDG { class LinearContext { public: LinearContext( void ); // constructor ~LinearContext( void ); // destructor operator cholmod_common*( void ); // allows LinearContext to be treated as a cholmod_common* protected: cholmod_common context; }; } #endif ================================================ FILE: BaseCode/include/LinearEquation.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearEquation.h // ----------------------------------------------------------------------------- // // LinearEquation represents an equation with an arbitrary linear polynomial on // both the left- and right-hand side. It is primarily used while building a // LinearSystem. For convenience, operator== is overloaded so that the user // can construct a LinearEquation by writing something that looks much like the // usual mathematical syntax for a linear equation. For example, // // LinearEquation eqn = ( x + 2*y == 3*z ); // // builds the linear equation x + 2y = 3z. // #ifndef DDG_LINEAREQUATION_H #define DDG_LINEAREQUATION_H #include "LinearPolynomial.h" namespace DDG { class LinearEquation { public: LinearPolynomial lhs; // left-hand side LinearPolynomial rhs; // right-hand side }; LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ); // constructs a linear equation with the specified left- and right-hand side } #endif ================================================ FILE: BaseCode/include/LinearPolynomial.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearPolynomial.h // ----------------------------------------------------------------------------- // // LinearPolynomial represents an affine function of the form // // f(x1,x2,...,xn) = c1 x1 + c2 x2 + ... + cn xn + d // // where the xi are real-valued variables with real coefficients ci, and d is // a real constant. The variables and their coefficients are represented using // instances of the Variable class. LinearPolynomial implements all the usual // algebraic operations on affine functions, as well as type conversions from // more elementary types (scalars, single variables, etc.). // // Importantly, variables used in a LinearPolynomial should *not* be deallocated // while the polynomial is still in use -- LinearPolynomial stores only a // reference to these variables so that the solution to a linear system can be // automatically copied back into the variables. // #ifndef DDG_LINEARPOLYNOMIAL_H #define DDG_LINEARPOLYNOMIAL_H #include #include #include "Variable.h" namespace DDG { class LinearPolynomial { public: LinearPolynomial( void ); // constructs the zero function LinearPolynomial( double c ); // constructs the constant function with value c LinearPolynomial( Variable& v ); // constructs a function with a single variable v const LinearPolynomial& operator=( double c ); // assigns the constant function with value c const LinearPolynomial& operator=( Variable& v ); // assigns a function with a single variable v void operator+=( double c ); void operator-=( double c ); void operator*=( double c ); void operator/=( double c ); // adds, subtract, multiplies, or divides by a constant void operator+=( Variable& v ); void operator-=( Variable& v ); // increments or decrements by a single variable v void operator+=( const LinearPolynomial& p ); void operator-=( const LinearPolynomial& p ); // increments or decrements by an affine function LinearPolynomial operator-( void ) const; // returns the additive inverse (i.e., negation) double evaluate( void ) const; // evaluates the function using the current values of its variables std::map linearTerms; // list of linear terms double constantTerm; // constant term }; LinearPolynomial operator+( double c, Variable& v ); // sum LinearPolynomial operator+( Variable& v, double c ); // sum LinearPolynomial operator-( double c, Variable& v ); // difference LinearPolynomial operator-( Variable& v, double c ); // difference LinearPolynomial operator*( double c, Variable& v ); // product LinearPolynomial operator*( Variable& v, double c ); // product LinearPolynomial operator/( Variable& v, double c ); // quotient // algebraic operations between single variables and constants LinearPolynomial operator+( Variable& v1, Variable& v2 ); // sum LinearPolynomial operator-( Variable& v1, Variable& v2 ); // difference // algebraic operations between pairs of variables LinearPolynomial operator+( const LinearPolynomial& p, double c ); // sum LinearPolynomial operator+( double c, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, double c ); // difference LinearPolynomial operator-( double c, const LinearPolynomial& p ); // difference LinearPolynomial operator*( const LinearPolynomial& p, double c ); // product LinearPolynomial operator*( double c, const LinearPolynomial& p ); // product LinearPolynomial operator/( const LinearPolynomial& p, double c ); // quotient // algebraic operations between polynomials and constants LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ); // sum LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ); // difference LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ); // difference // algebraic operations between polynomials and single variables LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ); // sum LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ); // difference // algebraic operations between pairs of polynomials std::ostream& operator<<( std::ostream& os, const LinearPolynomial& p ); // prints the symbolic representation of a polynomial (all variables must be named) } #endif ================================================ FILE: BaseCode/include/LinearSystem.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearSystem.h // ----------------------------------------------------------------------------- // // LinearSystem represents a system of linear equations expressed in terms of // instances of the Variable class. The main idea is to make it easy to // construct and solve linear systems without explicitly think about variable // indices, matrix layout, etc. (This kind of abstraction is particularly // useful for debugging and rapid prototyping.) See the documentation for // examples of building linear and solving systems. // // Importantly, any variable used by a LinearSystem should not be deallocated // while the system is still in use, because the method LinearSystem::solve() // automatically copies the solution back into the variables used to define the // equations. (In the future variables may become reference-counted in order // to avoid this issue.) // // Note that LinearSystem::solve() uses a general-purpose linear solver (namely // the sparse QR factorization found in SuiteSparse) that is quite fast but may // not always be your best option. To improve performance you may want to // build the system explicitly using an instance of SparseMatrix and call a // more specialized solver. (In the future there may be options for specifying // that a LinearSystem is, e.g., symmetric and positive-definite.) // #ifndef DDG_LINEARSYSTEM_H #define DDG_LINEARSYSTEM_H #include #include "LinearEquation.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "Real.h" namespace DDG { class LinearSystem { public: void clear( void ); // removes all equations from the system void push_back( const LinearEquation& e ); // appends the equation e to the sytem void solve( void ); // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution std::vector equations; // the collection of equations defining the system protected: void convertEquations( void ); void indexVariables( void ); void buildSparseMatrix( void ); void buildRightHandSide( void ); void computeSolution( void ); int nEquations; int nVariables; std::vector currentEquations; std::map index; SparseMatrix A; DenseMatrix x; DenseMatrix b; }; } #endif ================================================ FILE: BaseCode/include/Mesh.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Mesh.h // ----------------------------------------------------------------------------- // // Mesh represents a polygonal surface mesh using the halfedge data structure. // It is essentially a large collection of disjoint vertices, edges, and faces // that are ``glued together'' by halfedges which encode connectivity (see // the documentation for an illustration). By construction, the halfedge data // structure cannot represent nonorientable surfaces or meshes with nonmanifold // edges. // // Mesh elements are referenced using iterators -- common usage of these // iterators is to either traverse an entire vector of mesh elements: // // // visit all vertices // for( VertexIter i = vertices.begin(); i != vertices.end(); i++ ) // { // //... // } // // or to perform a local traversal over the neighborhood of some mesh element: // // // visit both halfedges of edge e // HalfEdgeIter he = e->he; // do // { // // ... // // he = he->flip; // } // while( he != e->he ); // // (See Types.h for an explicit definition of iterator types.) // // Meshes with boundary are handled by creating an additional face for each // boundary loop (the method Face::isBoundary() determines whether a given // face is a boundary loop). Isolated vertices (i.e., vertiecs not contained // in any edge or face) reference a dummy halfedge and can be checked via // the method Vertex::isIsolated(). // #ifndef DDG_MESH_H #define DDG_MESH_H #include #include #include "HalfEdge.h" #include "Vertex.h" #include "Edge.h" #include "Face.h" #include "SparseMatrix.h" namespace DDG { class Mesh { public: Mesh( void ); // constructs an empty mesh Mesh( const Mesh& mesh ); // constructs a copy of mesh const Mesh& operator=( const Mesh& mesh ); // copies mesh int read( const std::string& filename ); // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error int write( const std::string& filename ) const; // writes a mesh to a Wavefront OBJ file; return value is nonzero // only if there was an error bool reload( void ); // reloads a mesh from disk using the most recent input filename void normalize( void ); // centers around the origin and rescales to have unit radius double area( void ) const; // returns total mesh area double meanEdgeLength( void ) const; // returns mean edge lenght std::vector halfedges; std::vector vertices; std::vector edges; std::vector faces; std::vector boundaries; // storage for mesh elements protected: std::string inputFilename; void indexElements( void ); // assigns a unique, 0-based index to each mesh element }; } #endif ================================================ FILE: BaseCode/include/MeshIO.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- MeshIO.h // ----------------------------------------------------------------------------- // // MeshIO handles input/output operations for Mesh objects. Currently the only // supported mesh format is Wavefront OBJ -- for a format specification see // // http://en.wikipedia.org/wiki/Wavefront_.obj_file // // Note that vertex normals and material properties are currently ignored. // #ifndef DDG_MESHIO_H #define DDG_MESHIO_H #include #include #include #include namespace DDG { class Mesh; class Index; class MeshData; class MeshIO { public: static int read( std::istream& in, Mesh& mesh ); // reads a mesh from a valid, open input stream in static void write( std::ostream& out, const Mesh& mesh ); // writes a mesh to a valid, open output stream out protected: static int readMeshData( std::istream& in, MeshData& data ); static void readPosition( std::stringstream& ss, MeshData& data ); static void readTexCoord( std::stringstream& ss, MeshData& data ); static void readNormal ( std::stringstream& ss, MeshData& data ); static void readFace ( std::stringstream& ss, MeshData& data ); static Index parseFaceIndex( const std::string& token ); static void preallocateMeshElements( const MeshData& data, Mesh& mesh ); static int buildMesh( const MeshData& data, Mesh& mesh ); static void checkIsolatedVertices( const Mesh& Mesh ); static void checkNonManifoldVertices( const Mesh& Mesh ); }; } #endif ================================================ FILE: BaseCode/include/Quaternion.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Quaternion.h // ----------------------------------------------------------------------------- // // Quaternion represents an element of the quaternions, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // Hamilton product is expressed using the * operator: // // Quaternion p, q, r; // r = q * p; // // and conjugation is expressed using the method Quaternion::conj(): // // Quaternion q; // double normQSquared = -q.conj()*q; // // Individual components can be accessed in several ways: the real and imaginary // parts can be accessed using the methods Quaternion::re() and Quaternion::im(): // // Quaternion q; // double a = q.re(); // Vector b = q.im(); // // or by index: // // Quaternion q; // double a = q[0]; // double bi = q[1]; // double bj = q[2]; // double bk = q[3]; // #ifndef DDG_QUATERNION_H #define DDG_QUATERNION_H #include "Vector.h" #include "Complex.h" #include namespace DDG { class Quaternion { public: Quaternion( void ); // initializes all components to zero Quaternion( const Quaternion& q ); // initializes from existing quaternion Quaternion( double s, double vi, double vj, double vk ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s, const Vector& v ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s ); // initializes purely real quaternion with specified real (s) component (imaginary part is zero) Quaternion( const Vector& v ); // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) Quaternion( const Complex& z ); // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k const Quaternion& operator=( double s ); // assigns a purely real quaternion with real value s const Quaternion& operator=( const Vector& v ); // assigns a purely real quaternion with imaginary value v double& operator[]( int index ); // returns reference to the specified component (0-based indexing: r, i, j, k) const double& operator[]( int index ) const; // returns const reference to the specified component (0-based indexing: r, i, j, k) void toMatrix( double Q[4][4] ) const; // builds 4x4 matrix Q representing (left) quaternion multiplication double& re( void ); // returns reference to double part const double& re( void ) const; // returns const reference to double part Vector& im( void ); // returns reference to imaginary part const Vector& im( void ) const; // returns const reference to imaginary part Quaternion operator+( const Quaternion& q ) const; // addition Quaternion operator-( const Quaternion& q ) const; // subtraction Quaternion operator-( void ) const; // negation Quaternion operator*( double c ) const; // right scalar multiplication Quaternion operator/( double c ) const; // scalar division void operator+=( const Quaternion& q ); // addition / assignment void operator+=( double c ); // addition / assignment of pure real void operator-=( const Quaternion& q ); // subtraction / assignment void operator-=( double c ); // subtraction / assignment of pure real void operator*=( double c ); // scalar multiplication / assignment void operator/=( double c ); // scalar division / assignment Quaternion operator*( const Quaternion& q ) const; // Hamilton product void operator*=( const Quaternion& q ); // Hamilton product / assignment Quaternion conj( void ) const; // conjugation Quaternion inv( void ) const; // inverse double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Quaternion unit( void ) const; // returns unit quaternion void normalize( void ); // divides by Euclidean length protected: double s; // scalar (double) part Vector v; // vector (imaginary) part }; Quaternion operator*( double c, const Quaternion& q ); // left scalar multiplication std::ostream& operator<<( std::ostream& os, const Quaternion& q ); // prints components } #endif ================================================ FILE: BaseCode/include/Real.h ================================================ #ifndef DDG_REAL_H #define DDG_REAL_H namespace DDG { class Real { public: Real( double x = 0. ); // constructs real number with value x operator double( void ) const; // type cast to double void operator+=( double x ); // increment void operator-=( double x ); // decrement void operator*=( double x ); // multiply void operator/=( double x ); // divide Real conj( void ) const; // simply returns the value (for compatibility w/ complex numbers) Real inv( void ) const; // returns inverse double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Real unit( void ) const; // returns number with unit norm and same sign protected: double value; // value }; } #endif ================================================ FILE: BaseCode/include/Shader.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Shader.h // ----------------------------------------------------------------------------- // // Shader encapsulates the functionality of a shader program written in // the OpenGL Shader Language (GLSL). Basic usage is to read a collection // of source files to disk and enable the shader before making draw calls. // For instance, during initialization one might write // // Shader shader; // shader.loadVertex( "vertex.glsl" ); // shader.loadFragment( "fragment.glsl" ); // // and in the main draw routine write // // shader.enable(); // // draw some stuff // shader.disable(); // #ifndef DDG_SHADER_H #define DDG_SHADER_H #include #include namespace DDG { class Shader { public: Shader( void ); // constructor -- shader is initially invalid ~Shader( void ); // destructor void loadVertex( const char* filename ); // read vertex shader from GLSL source file void loadFragment( const char* filename ); // read fragment shader from GLSL source file void loadGeometry( const char* filename ); // read geometry shader from GLSL source file void enable( void ); // uses this shader for rendering void disable( void ) const; // uses the fixed-function pipeline for rendering operator GLuint( void ) const; // returns the ID of this shader program (for calls to OpenGL) protected: void load( GLenum shaderType, const char* filename, GLuint& shader ); bool readSource( const char* filename, std::string& source ); GLuint vertexShader; GLuint fragmentShader; GLuint geometryShader; GLuint program; bool linked; }; } #endif ================================================ FILE: BaseCode/include/SparseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- SparseMatrix.h // ----------------------------------------------------------------------------- // // SparseMatrix represents an m by n (real or complex) matrix where only // nonzero entries are stored explicitly. This class is most commonly // used to represent the linear term in sparse linear systems (i.e., the matrix // part). // // A real or complex matrix is allocated via // // SparseMatrix A( m, n ); // SparseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // SparseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a SparseMatrix returns a // cholmod_sparse* which can be used by routines in SuiteSparse. For basic // operations, however, you should not need to access this pointer explicitly -- // see the solve() method below. // // Internally SparseMatrix stores nonzero entries in a heap data structure; the // amortized cost of insertion is therefore no worse than the sorting cost of // putting the matrix in compressed-column order. // #ifndef DDG_SPARSE_MATRIX_H #define DDG_SPARSE_MATRIX_H #include #include #include #include "Types.h" namespace DDG { template class SparseMatrix { public: SparseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix SparseMatrix( const SparseMatrix& B ); // copy constructor ~SparseMatrix( void ); // destructor const SparseMatrix& operator=( const SparseMatrix& B ); // copies B const SparseMatrix& operator=( cholmod_sparse* B ); // copies a cholmod_sparse* into a SparseMatrix; // takes responsibility for deallocating B void resize( int m, int n ); // clears and resizes to mxn matrix SparseMatrix transpose( void ) const; // returns the transpose of this matrix cholmod_sparse* to_cholmod( void ); // returns pointer to copy of matrix in compressed-column CHOLMOD format SparseMatrix operator*( const SparseMatrix& B ) const; // returns product of this matrix with sparse B DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with dense B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c void operator+=( const SparseMatrix& B ); // adds B to this matrix void operator-=( const SparseMatrix& B ); // subtracts B from this matrix SparseMatrix operator+( const SparseMatrix& B ) const; // returns sum of this matrix with B SparseMatrix operator-( const SparseMatrix& B ) const; // returns difference of this matrix with B int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val ); // sets all nonzero elements val SparseMatrix inverse( void ) const; // returns inverse -- for diagonal matrices only // (assertion occurs for non-diagonal matrices) static SparseMatrix identity( int N ); // returns the N x N identity matrix DenseMatrix full( void ) const; // converts to a dense matrix T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element (uses 0-based indexing) // TODO for legibility, replace w/ type where entries are named "row, // TODO col" instead of "first, second" (especially since we adopt the // TODO unorthodox convention of storing the column first) typedef std::pair EntryIndex; // convenience type for an entry index; note that we store column THEN // row, which makes it easier to build compressed column format typedef std::map EntryMap; typedef typename EntryMap::iterator iterator; typedef typename EntryMap::const_iterator const_iterator; // convenience types for storing and accessing entries iterator begin( void ); const_iterator begin( void ) const; iterator end( void ); const_iterator end( void ) const; // return iterators to first and last nonzero entries void shift( double c ); // adds c times the identity matrix to this matrix protected: int m, n; EntryMap data; cholmod_sparse* cData; void allocateSparse( void ); void setEntry( const_iterator e, int i, double* pr ); }; template SparseMatrix operator*( const SparseMatrix& A, const T& c ); // right scalar multiplication template SparseMatrix operator*( const T& c, const SparseMatrix& A ); // left scalar multiplication template SparseMatrix operator/( const SparseMatrix& A, const T& c ); // scalar division template std::ostream& operator << (std::ostream& os, const SparseMatrix& o); // prints entries template class SparseFactor { public: SparseFactor( void ); ~SparseFactor( void ); void build( SparseMatrix& A ); // factorizes positive-definite matrix A using CHOLMOD bool valid( void ) const; // returns true if the factor has been built; false otherwise cholmod_factor* to_cholmod( void ); // returns pointer to underlying cholmod_factor data structure protected: cholmod_factor *L; }; template void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse QR factorization template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse LU factorization template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ); // backsolves the prefactored positive definite sparse linear system LL'x = b template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ); // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ); // returns the max residual of the linear problem A x = b relative to the largest entry of the solution template double residual( const SparseMatrix& A, const DenseMatrix& x ); // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda B x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns /<(B-EE^T)x,x> } #include "SparseMatrix.inl" #endif ================================================ FILE: BaseCode/include/Types.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Types.h // ----------------------------------------------------------------------------- // // This file contains forward declarations of common types and definitions of // convenience types for standard iterators. // #ifndef DDG_TYPES_H #define DDG_TYPES_H #include #include #include namespace DDG { // forward declarations class Camera; class Complex; class Edge; class Face; class HalfEdge; class Image; class LinearContext; class LinearEquation; class LinearPolynomial; class LinearSystem; class Mesh; class MeshIO; class Quaternion; class Real; class Shader; class Variable; class Vector; class Vertex; class Viewer; template class DenseMatrix; template class SparseMatrix; // convenience types for iterators typedef std::map::iterator TermIter; typedef std::map::const_iterator TermCIter; typedef std::vector::iterator PolyIter; typedef std::vector::const_iterator PolyCIter; typedef std::vector::iterator EqnIter; typedef std::vector::const_iterator EqnCIter; typedef std::map::iterator IndexIter; typedef std::map::const_iterator IndexCIter; typedef std::vector::iterator HalfEdgeIter; typedef std::vector::const_iterator HalfEdgeCIter; typedef std::vector::iterator VertexIter; typedef std::vector::const_iterator VertexCIter; typedef std::vector::iterator EdgeIter; typedef std::vector::const_iterator EdgeCIter; typedef std::vector::iterator FaceIter; typedef std::vector::const_iterator FaceCIter; } #endif ================================================ FILE: BaseCode/include/Utility.h ================================================ #ifndef DDG_UTILITY_H #define DDG_UTILITY_H #include #include "Utility.h" #include "Complex.h" namespace DDG { inline double sqr( double x ) { return x*x; } inline double unitRand( void ) { const double rRandMax = 1. / (double) RAND_MAX; return rRandMax * (double) rand(); } inline double seconds( int t0, int t1 ) { return (double)(t1-t0) / (double) CLOCKS_PER_SEC; } } namespace DDGConstants { static DDG::Complex ii( 0., 1. ); } #endif ================================================ FILE: BaseCode/include/Variable.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Variable.h // ----------------------------------------------------------------------------- // // Variable represents a variable that can be used to define a (linear or // nonlinear) system of equations. Its main feature is that it can be used // both as an abstract variable (e.g., when used to define an equation) but can // also store a definite numerical value. For instance, suppose we define a // linear polynomial // // Variable x, y; // LinearPolynomial p = x + 2*y; // // Now by assigning different numerical values to x and y, we can evaluate the // polynomial at different points: // // *x = 1; // *y = 2; // cout << p.evaluate() << endl; // // *x = 3; // *y = 4; // cout << p.evaluate() << endl; // // In general the dereference operator (*) accesses the numerical value. // Variables can also be named in order to aid with debugging. For instance, // // Variable x( "x" ); // Variable y( "y" ); // Polynomial p = x + 2*y; // cout << p << endl; // // will print out something like "x+2*y". // // The "fixed" flag in a variable refers to whether it is held constant while // solving a system of equations -- see the documentation for further discussion. // #ifndef DDG_VARIABLE_H #define DDG_VARIABLE_H #include namespace DDG { class Variable { public: Variable( double value = 0., bool fixed = false ); // initialize a variable which has value zero and is not fixed by default Variable( std::string name, double value = 0., bool fixed = false ); // initialize a named variable which has value zero and is not fixed by default double& operator*( void ); // returns a reference to the numerical value const double& operator*( void ) const; // returns a const reference to the numerical value std::string name; // names the variable (for display output) double value; // numerical value bool fixed; // true if a variable is held constant while solving a system of equations }; } #endif ================================================ FILE: BaseCode/include/Vector.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vector.h // ----------------------------------------------------------------------------- // // Vector represents an element of Euclidean 3-space, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // inner product (i.e., scalar or dot product) is expressed using the global // method dot(): // // Vector u, v; // double cosTheta = dot( u, v ); // // and the cross product is expressed using the global method cross(): // // Vector u, v, w; // w = cross( u, v ); // // Individual components can be accessed in two ways: either directly via the // members x, y, and z: // // Vector v; // cout << v.x << endl; // cout << v.y << endl; // cout << v.z << endl; // // or by index: // // Vector v; // for( int i = 0; i < 3; i++ ) // { // cout << v[i] << endl; // } // #ifndef DDG_VECTOR_H #define DDG_VECTOR_H #include namespace DDG { class Vector { public: Vector(); // initializes all components to zero Vector( double x, double y, double z); // initializes with specified components Vector( const Vector& v ); // initializes from existing vector double& operator[] ( const int& index ); // returns reference to the specified component (0-based indexing: x, y, z) const double& operator[] ( const int& index ) const; // returns const reference to the specified component (0-based indexing: x, y, z) Vector operator+( const Vector& v ) const; // addition Vector operator-( const Vector& v ) const; // subtraction Vector operator-( void ) const; // negation Vector operator*( const double& c ) const; // right scalar multiplication Vector operator/( const double& c ) const; // scalar division void operator+=( const Vector& v ); // addition / assignment void operator-=( const Vector& v ); // subtraction / assignment void operator*=( const double& c ); // scalar multiplication / assignment void operator/=( const double& c ); // scalar division / assignment double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Vector unit( void ) const; // returns unit vector void normalize( void ); // divides by Euclidean length Vector abs( void ) const; // returns vector containing magnitude of each component double x, y, z; // components }; Vector operator* ( const double& c, const Vector& v ); // left scalar multiplication double dot( const Vector& u, const Vector& v ); // dot product (a.k.a. inner or scalar product) Vector cross( const Vector& u, const Vector& v ); // cross product std::ostream& operator << (std::ostream& os, const Vector& o); // prints components } #endif ================================================ FILE: BaseCode/include/Vertex.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vertex.h // ----------------------------------------------------------------------------- // // Vertex stores attributes associated with a mesh edge. The iterator he // points to its "outgoing" halfedge. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_VERTEX_H #define DDG_VERTEX_H #include "Vector.h" #include "Types.h" namespace DDG { class Vertex { public: HalfEdgeIter he; // points to the "outgoing" halfedge Vector position; // location of vertex in Euclidean 3-space int index; // unique integer ID in the range 0, ..., nVertices-1 bool tag; // true if vertex is selected by the user; false otherwise Vertex() : index(0), tag(false) { } double area( void ) const; // returns the barycentric area associated with this vertex Vector normal( void ) const; // returns the vertex normal bool isIsolated( void ) const; // returns true if the vertex is not contained in any face or edge; false otherwise int valence( void ) const; // returns the number of incident faces / edges void toggleTag(); // toggle vertex tag }; } #endif ================================================ FILE: BaseCode/include/Viewer.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Viewer.h // ----------------------------------------------------------------------------- // // Viewer provides a graphical user interface (GUI) for inspecting and // interacting with a Mesh object. Viewer methods are static in order // to make them compatible with GLUT callbacks. // #ifndef DDG_VIEWER_H #define DDG_VIEWER_H #include #include "Mesh.h" #include "Camera.h" #include "Shader.h" namespace DDG { class Viewer { public: static void init( void ); // displays the viewer until the program ends static Mesh mesh; // surface mesh visualized by Viewer protected: // init static void initGLUT( void ); static void initGLSL( void ); // GLUT callbacks static void display( void ); static void idle( void ); static void keyboard( unsigned char c, int x, int y ); static void special( int i, int x, int y ); static void mouse( int button, int state, int x, int y ); static void motion( int x, int y ); static void menu( int value ); static void view( int value ); // menu functions static void mProcess( void ); static void mResetMesh( void ); static void mWriteMesh( void ); static void mExit( void ); static void mWireframe( void ); static void mZoomIn( void ); static void mZoomOut( void ); static void mScreenshot( void ); // unique identifiers for menus enum { menuProcess, menuResetMesh, menuWriteMesh, menuExit, menuWireframe, menuZoomIn, menuZoomOut, menuScreenshot }; // draw routines static void setGL( void ); static void setLighting( void ); static void setMeshMaterial( void ); static void callDisplayList( void ); static void updateDisplayList( void ); static void drawScene( void ); static void drawPolygons( void ); static void drawWireframe( void ); static void drawVertices( void ); static void drawSelectedVertices( void ); static void drawIsolatedVertices( void ); static void pickVertex(int x, int y); static void storeViewerState( void ); static void restoreViewerState( void ); static int windowSize[2]; static bool renderWireframe; // draw wireframe static Camera camera; // keeps track of view state static GLuint surfaceDL; // display list for mesh static Shader shader; // shader used to determine appearance of surface }; } #endif ================================================ FILE: BaseCode/obj/.empty ================================================ ================================================ FILE: BaseCode/shaders/fragment.glsl ================================================ uniform vec3 eye; uniform vec3 light; varying vec3 position; varying vec3 normal; float diffuse( vec3 N, vec3 L ) { return max( 0., dot( N, L )); } float specular( vec3 N, vec3 L, vec3 E ) { const float shininess = 8.; vec3 R = 2.*dot(L,N)*N - L; return pow( max( 0., dot( R, E )), shininess ); } float fresnel( vec3 N, vec3 E ) { const float sharpness = 10.; float NE = max( 0., dot( N, E )); return pow( sqrt( 1. - NE*NE ), sharpness ); } void main() { vec3 N = normalize( normal ); vec3 L = normalize( light - position ); vec3 E = normalize( eye - position ); vec3 R = 2.*dot(L,N)*N - L; vec3 one = vec3( 1., 1., 1. ); gl_FragColor.rgb = diffuse(N,L)*gl_Color.rgb + .5*specular(N,L,E)*one + .5*fresnel(N,E)*one; gl_FragColor.a = 1.; } ================================================ FILE: BaseCode/shaders/vertex.glsl ================================================ varying vec3 position; varying vec3 normal; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_FrontColor = gl_Color; position = gl_Vertex.xyz; normal = gl_Normal.xyz; } ================================================ FILE: BaseCode/src/Camera.cpp ================================================ #include #include #include using namespace std; #include "Camera.h" namespace DDG { Camera :: Camera( void ) : pClick( 1. ), pDrag( 1. ), pLast( 1. ), rLast( 1. ), momentum( 1. ), zoom( 1. ) {} Quaternion Camera :: clickToSphere( int x, int y ) { GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); int w = viewport[2]; int h = viewport[3]; Quaternion p( 0., 2. * (double) x / (double) w - 1., 2. * (double) y / (double) h - 1., 0. ); if( p.norm2() > 1. ) { p.normalize(); p.im().z = 0.; } else { p.im().z = sqrt( 1. - p.norm2() ); } return p; } Quaternion Camera :: currentRotation( void ) const { return ( pDrag * pClick.conj() ) * rLast; } void Camera :: setView( void ) const { Quaternion r = ( pDrag * pClick.conj() ) * rLast; double w = r[0]; double x = r[1]; double y = r[2]; double z = r[3]; GLdouble M[16] = { 1.-2.*y*y-2.*z*z, 2.*x*y+2.*w*z, 2.*x*z-2.*w*y, 0., 2.*x*y-2.*w*z, 1.-2.*x*x-2.*z*z, 2.*y*z+2.*w*x, 0., 2.*x*z+2.*w*y, 2.*y*z-2.*w*x, 1.-2.*x*x-2.*y*y, 0., 0., 0., 0., 1. }; glMatrixMode( GL_MODELVIEW ); glMultMatrixd( M ); } void Camera :: mouse( int button, int state, int x, int y ) { if( state == GLUT_DOWN ) { pClick = pDrag = pLast = clickToSphere( x, y ); momentum = 1.; } if( state == GLUT_UP ) { double timeSinceDrag = ( clock() - tLast ) / (double) CLOCKS_PER_SEC; if( timeSinceDrag < .1 ) { momentum = pDrag * pLast.conj(); momentum = ( .03 * momentum + .97 ).unit(); } else { momentum = 1.; } rLast = pDrag * pClick.conj() * rLast; pClick = pDrag = 1.; } } void Camera :: motion( int x, int y ) { tLast = clock(); pLast = pDrag; pDrag = clickToSphere( x, y ); } void Camera :: idle( void ) { // get time since last idle event static int t0 = clock(); int t1 = clock(); double dt = (t1-t0) / (double) CLOCKS_PER_SEC; rLast = momentum * rLast; momentum = ( (1.-.5*dt) * momentum + .5*dt ).unit(); zoom += vZoom*dt; vZoom *= max( 0., 1.-5.*dt ); t0 = t1; } void Camera :: zoomIn( void ) { vZoom -= 0.5; } void Camera :: zoomOut( void ) { vZoom += 0.5; } } ================================================ FILE: BaseCode/src/Complex.cpp ================================================ #include "Complex.h" #include #include using namespace std; namespace DDG { Complex::Complex( double a, double b ) // constructs number a+bi : re( a ), im( b ) {} void Complex::operator+=( const Complex& z ) // add z { re += z.re; im += z.im; } void Complex::operator-=( const Complex& z ) // subtract z { re -= z.re; im -= z.im; } void Complex::operator*=( const Complex& z ) // Complex multiply by z { double a = re; double b = im; double c = z.re; double d = z.im; re = a*c-b*d; im = a*d+b*c; } void Complex::operator*=( double r ) // scalar multiply by r { re *= r; im *= r; } void Complex::operator/=( double r ) // scalar divide by r { re /= r; im /= r; } void Complex::operator/=( const Complex& z ) // scalar divide by r { *this *= z.inv(); } Complex Complex::operator-( void ) const { return Complex( -re, -im ); } Complex Complex::conj( void ) const // returns Complex conjugate { return Complex( re, -im ); } Complex Complex::inv( void ) const // returns inverse { return this->conj() / this->norm2(); } double Complex::arg( void ) const // returns argument { return atan2( im, re ); } double Complex::norm( void ) const // returns norm { return sqrt( re*re + im*im ); } double Complex::norm2( void ) const // returns norm squared { return re*re + im*im; } Complex Complex::unit( void ) const // returns complex number with unit norm and same modulus { return *this / this->norm(); } Complex Complex::exponential( void ) const // complex exponentiation { return exp( re ) * Complex( cos( im ), sin( im )); } Complex operator+( const Complex& z1, const Complex& z2 ) // binary addition { Complex z = z1; z += z2; return z; } Complex operator-( const Complex& z1, const Complex& z2 ) // binary subtraction { Complex z = z1; z -= z2; return z; } Complex operator*( const Complex& z1, const Complex& z2 ) // binary Complex multiplication { Complex z = z1; z *= z2; return z; } Complex operator*( const Complex& z, double r ) // right scalar multiplication { Complex zr = z; zr *= r; return zr; } Complex operator*( double r, const Complex& z ) // left scalar multiplication { return z*r; } Complex operator/( const Complex& z, double r ) // scalar division { Complex zr = z; zr /= r; return zr; } Complex operator/( const Complex& z1, const Complex& z2 ) // complex division { Complex z = z1; z /= z2; return z; } double dot( const Complex& z1, const Complex& z2 ) { return z1.re*z2.re + z1.im*z2.im; } double cross( const Complex& z1, const Complex& z2 ) { return z1.re*z2.im - z1.im*z2.re; } std::ostream& operator<<( std::ostream& os, const Complex& z ) // prints components { if( z.im > 0 ) { os << z.re << " + " << z.im << "i"; } else if( z.im < 0 ) { os << z.re << " - " << -z.im << "i"; } else { os << z.re; } return os; } } ================================================ FILE: BaseCode/src/DenseMatrix.cpp ================================================ #include "DenseMatrix.h" namespace DDG { template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i] = data[i]; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_COMPLEX, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i*2+0] = data[i].re; x[i*2+1] = data[i].im; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { assert( nColumns() == 1 ); if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } cData = cholmod_l_allocate_dense( m*4, 1, m*4, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { x[i*4+k] = data[i][k]; } } return cData; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = x[i]; } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = Complex( x[i*2+0], x[i*2+1] ); } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); assert( B->ncol == 1 ); assert( B->nrow%4 == 0 ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow/4; n = 1; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m; i++ ) { data[i] = Quaternion( x[i*4+0], x[i*4+1], x[i*4+2], x[i*4+3] ); } return *this; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 3; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { double x = o(i,j); if( x == 0. ) { os << " 0"; for( int k = 0; k < p+6; k++ ) { os << " "; } } else if( x > 0. ) { os << " " << x << " "; } else { os << x << " "; } } os << "]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { Complex z = o(i,j); if( z.re == 0. ) { os << " 0"; for( int k = 0; k < p+5; k++ ) { os << " "; } } else if( z.re > 0. ) { os << " " << z.re; } else { os << z.re; } if( z.im == 0 ) { os << " "; } else if( z.im >= 0 ) { os << "+"; } else { os << "-"; } if( z.im == 0. ) { for( int k = 0; k < p+8; k++ ) { os << " "; } } else { os << abs( z.im ) << "i "; } } os << " ]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "["; for( int j = 0; j < o.nColumns(); j++ ) { Quaternion q = o(i,j); os << " " << q; } os << " ]" << endl; } return os; } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i] = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i].re = 2.*unitRand() - 1.; data[i].im = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { data[i][k] = 2.*unitRand() - 1.; } } } } ================================================ FILE: BaseCode/src/DenseMatrix.inl ================================================ #include #include #include #include using namespace std; #include "DenseMatrix.h" #include "LinearContext.h" #include "Quaternion.h" #include "SparseMatrix.h" #include "Utility.h" namespace DDG { extern LinearContext context; template DenseMatrix :: DenseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) { data.resize( m*n ); zero(); } template DenseMatrix :: DenseMatrix( const DenseMatrix& A ) // copy constructor : cData( NULL ) { *this = A; } template DenseMatrix :: ~DenseMatrix( void ) // destructor { if( cData != NULL ) { cholmod_l_free_dense( &cData, context ); } } template DenseMatrix DenseMatrix :: transpose( void ) const { const DenseMatrix& A( *this ); DenseMatrix AT( n, m ); for( int i = 0; i < n; i++ ) for( int j = 0; j < m; j++ ) { AT(i,j) = A(j,i).conj(); } return AT; } template SparseMatrix DenseMatrix::sparse( void ) // converts to a sparse matrix { SparseMatrix B; B = cholmod_l_dense_to_sparse( this->to_cholmod(), true, context ); return B; } template DenseMatrix DenseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); DenseMatrix AB( A.nRows(), B.nColumns() ); for( int i = 0; i < A.nRows(); i++ ) for( int j = 0; j < B.nColumns(); j++ ) for( int k = 0; k < A.nColumns(); k++ ) { AB( i, j ) += A( i, k ) * B( k, j ); } return AB; } template void DenseMatrix :: operator*=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c; } } template void DenseMatrix :: operator/=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c.inv(); } } template DenseMatrix DenseMatrix :: operator+( const DenseMatrix& B ) const // returns sum of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) + B(i,j); } return C; } template void DenseMatrix :: operator+=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) += B(i,j); } } template DenseMatrix DenseMatrix :: operator-( const DenseMatrix& B ) const // returns difference of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) - B(i,j); } return C; } template void DenseMatrix :: operator-=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) -= B(i,j); } } template DenseMatrix operator*( const T& c, const DenseMatrix& A ) { DenseMatrix cA = A; cA *= c; return cA; } template DenseMatrix operator*( const DenseMatrix& A, double c ) { return c*A; } template DenseMatrix operator/( const DenseMatrix& A, double c ) { DenseMatrix Ac = A; Ac /= c; return Ac; } template const DenseMatrix& DenseMatrix :: operator=( const DenseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template int DenseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int DenseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int DenseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void DenseMatrix :: zero( const T& val ) // sets all elements to val { for( int i = 0; i < m*n; i++ ) { data[i] = val; } } template double DenseMatrix :: norm( NormType type ) const { double r = 0.; if( type == lInfinity ) { for( int i = 0; i < m*n; i++ ) { r = max( r, data[i].norm() ); } } else if( type == lOne ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm(); } } else if( type == lTwo ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm2(); } r = sqrt( r ); } return r; } template void DenseMatrix :: normalize( void ) // divides by l2 norm { *this /= norm( lTwo ); } template T& DenseMatrix :: operator()( int row, int col ) { return data[row+m*col]; } template T DenseMatrix :: operator()( int row, int col ) const { return data[row+m*col]; } template T& DenseMatrix :: operator()( int index ) { return data[index]; } template T DenseMatrix :: operator()( int index ) const { return data[index]; } template T DenseMatrix::sum( void ) const // returns the sum of all entries { T total( 0., 0. ); for( int i = 0; i < m*n; i++ ) { total += data[i]; } return total; } template void DenseMatrix :: removeMean( void ) { T mean = 0.; int N = m*n; for( int i = 0; i < N; i++ ) { mean += data[i]; } mean /= (double) N; for( int i = 0; i < N; i++ ) { data[i] -= mean; } } template T dot( const DenseMatrix& x, const DenseMatrix& y ) // returns Euclidean inner product of x and y { return ( x.transpose() * y )(0); } template DenseMatrix DenseMatrix::operator-( void ) const // returns additive inverse of this matrix { const DenseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { B( i, j ) = -A( i, j ); } return B; } template T inner( const DenseMatrix& x, const DenseMatrix& y ) // standard inner product { T sum = 0.; assert( x.nRows() == y.nRows() && x.nColumns() == y.nColumns() ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * y(i); } return sum; } template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ) // inner product with respect a diagonal inner // product B represented as a dense vector { T sum = 0.; assert( x.nRows() == y.nRows() && x.nRows() == B.nRows() && x.nColumns() == 1 && B.nColumns() == 1 && y.nColumns() == 1 ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * B(i) * y(i); } return sum; } } ================================================ FILE: BaseCode/src/DiscreteExteriorCalculus.inl ================================================ #include "DiscreteExteriorCalculus.h" namespace DDG { template void HodgeStar0Form :: build( const Mesh& mesh, SparseMatrix& star0 ) // builds a diagonal matrix mapping primal discrete 0-forms // to dual discrete 2-forms { int nV = mesh.vertices.size(); star0 = SparseMatrix( nV, nV ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { int i = v->index; star0( i, i ) = v->area(); } } template void HodgeStar1Form :: build( const Mesh& mesh, SparseMatrix& star1 ) // builds a diagonal matrix mapping primal discrete 1-forms // to dual discrete 1-forms { int nE = mesh.edges.size(); star1 = SparseMatrix( nE, nE ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // get the cotangents of the two angles opposite this edge double cotAlpha = e->he->cotan(); double cotBeta = e->he->flip->cotan(); int i = e->index; star1( i, i ) = ( cotAlpha + cotBeta ) / 2.; } } template void HodgeStar2Form :: build( const Mesh& mesh, SparseMatrix& star2 ) // builds a diagonal matrix mapping primal discrete 2-forms // to dual discrete 2-forms { int nF = mesh.faces.size(); star2 = SparseMatrix( nF, nF ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { int i = f->index; star2( i, i ) = 1. / f->area(); } } template< class T > void ExteriorDerivative0Form :: build( const Mesh& mesh, SparseMatrix& d0 ) { int nV = mesh.vertices.size(); int nE = mesh.edges.size(); d0 = SparseMatrix( nE, nV ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // the row index is the index of the edge int r = e->index; // the column indices are the indices of the two // edge vertices -- orientation is determined by // the orientation of the edge's first half edge int ci = e->he->vertex->index; int cj = e->he->flip->vertex->index; d0( r, ci ) = -1.; d0( r, cj ) = 1.; } } template< class T > void ExteriorDerivative1Form :: build( const Mesh& mesh, SparseMatrix& d1 ) { int nE = mesh.edges.size(); int nF = mesh.faces.size(); d1 = SparseMatrix( nF, nE ); // visit each face for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { // the row index is the index of the face int r = f->index; // visit all edges of this face HalfEdgeCIter he = f->he; do { // the column index is the index of the current edge int c = he->edge->index; // relative orientation is determined by checking if // the current half edge is the first half edge of its // corresponding edge double s = ( he->edge->he == he ? 1. : -1. ); // set the entry for this edge d1( r, c ) = s; he = he->next; } while( he != f->he ); } } } ================================================ FILE: BaseCode/src/Edge.cpp ================================================ #include "Edge.h" #include "Mesh.h" namespace DDG { } ================================================ FILE: BaseCode/src/Face.cpp ================================================ #include "Face.h" #include "Mesh.h" #include "Vector.h" namespace DDG { double Face::area( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).norm() / 2.; } Vector Face::normal( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).unit(); } bool Face::isBoundary( void ) const { return he->onBoundary; } Vector Face :: circumcenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector n = he->rotatedEdge(); double h = 0.5*he->cotan(); return 0.5*(p0+p1) + h*n; } Vector Face :: barycenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return (p0 + p1 + p2)/3.; } } ================================================ FILE: BaseCode/src/HalfEdge.cpp ================================================ #include "HalfEdge.h" #include "Mesh.h" namespace DDG { double HalfEdge :: cotan( void ) const { if( onBoundary ) return 0.0; Vector p0 = next->next->vertex->position; Vector p1 = vertex->position; Vector p2 = next->vertex->position; Vector u = p1-p0; Vector v = p2-p0; return dot( u, v ) / cross( u, v ).norm(); } Vector HalfEdge :: rotatedEdge( void ) const { if( onBoundary ) return Vector(); Vector n = face->normal(); Vector p0 = vertex->position; Vector p1 = flip->vertex->position; return cross( n, p1-p0 ); } } ================================================ FILE: BaseCode/src/Image.cpp ================================================ #include #include #include #include using namespace std; #include "Image.h" namespace DDG { Image :: Image( int width, int height ) : w( width ), h( height ), pixels( w*h*3 ) {} float& Image :: operator()( int x, int y ) // accesses pixel (x,y) { return pixels[ x + y*w ]; } const float& Image :: operator()( int x, int y ) const // accesses pixel (x,y) { return pixels[ x + y*w ]; } float Image :: sample( float x, float y ) const // samples image at (x,y) using bilinear filtering { const Image& I( *this ); float ax = x - floor( x ); float ay = y - floor( y ); float bx = 1. - ax; float by = 1. - ay; int x0 = (int) floor( x ); int y0 = (int) floor( y ); int x1 = x0 + 1; int y1 = y0 + 1; clamp( x0, y0 ); clamp( x1, y1 ); return by * ( bx * I(x0,y0) + ax * I(x1,y0) ) + ay * ( bx * I(x0,y1) + ax * I(x1,y1) ) ; } int Image :: width( void ) const // returns image width { return w; } int Image :: height( void ) const // returns image height { return h; } class TGAHeader // header format for Truevision TGA images { public: char idFieldSize; char colorMapType; char dataTypeCode; short colorMapOrigin; short colorMapLength; char colorMapEntrySize; short xOrigin; short yOrigin; short width; short height; char bitsPerPixel; char imageSpecification; }; void Image :: read( const char* filename ) // loads an image file in Truevision TGA format // (must be uncompressed RGB image with 24 or 32 bits per pixel) { ifstream in( filename, ios_base::binary ); if( !in.is_open() ) { cerr << "Error: could not open file " << filename << " for input!" << endl; exit( 1 ); } // read header TGAHeader header; in.read( (char*) &(header.idFieldSize), 1 ); in.read( (char*) &(header.colorMapType), 1 ); in.read( (char*) &(header.dataTypeCode), 1 ); in.read( (char*) &(header.colorMapOrigin), 2 ); in.read( (char*) &(header.colorMapLength), 2 ); in.read( (char*) &(header.colorMapEntrySize), 1 ); in.read( (char*) &(header.xOrigin), 2 ); in.read( (char*) &(header.yOrigin), 2 ); in.read( (char*) &(header.width), 2 ); in.read( (char*) &(header.height), 2 ); in.read( (char*) &(header.bitsPerPixel), 1 ); in.read( (char*) &(header.imageSpecification), 1 ); w = header.width; h = header.height; // validate data type const char uncompressedRGB = 2; if( header.dataTypeCode != uncompressedRGB || ( header.bitsPerPixel != 24 && header.bitsPerPixel != 32 )) { cerr << "Error: input must be uncompressed RGB image with 24 or 32 bits per pixel." << endl; exit( 1 ); } // read identification field (unused) vector idField( header.idFieldSize ); in.read( &idField[0], header.idFieldSize ); // read color map data (unused) if( header.colorMapType == 1 ) { int bytesPerEntry = header.colorMapEntrySize / 8; int colorMapSize = header.colorMapLength * bytesPerEntry; vector colorMapData( colorMapSize ); in.read( &colorMapData[0], colorMapSize ); } // read pixel data int n = w*h*header.bitsPerPixel/8; vector pixelData( n ); in.read( (char*) &pixelData[0], n ); // convert pixel data to floating point pixels.resize( n ); for( int i = 0; i < n; i++ ) { pixels[i] = (double) pixelData[i] / 255.; } } void Image :: write( const char* filename ) const // writes an image file in Truevision TGA format // (uncompressed RGB image with 24 bits per pixel) { ofstream out( filename, ios_base::binary ); if( !out.is_open() ) { cerr << "Error: could not open file " << filename << " for output!" << endl; exit( 1 ); } TGAHeader header; header.idFieldSize = 0; header.colorMapType = 0; header.dataTypeCode = 2; header.colorMapOrigin = 0; header.colorMapLength = 0; header.colorMapEntrySize = 0; header.xOrigin = 0; header.yOrigin = 0; header.width = w; header.height = h; header.bitsPerPixel = 24; header.imageSpecification = 0; // write header out.write( (char*) &(header.idFieldSize), 1 ); out.write( (char*) &(header.colorMapType), 1 ); out.write( (char*) &(header.dataTypeCode), 1 ); out.write( (char*) &(header.colorMapOrigin), 2 ); out.write( (char*) &(header.colorMapLength), 2 ); out.write( (char*) &(header.colorMapEntrySize), 1 ); out.write( (char*) &(header.xOrigin), 2 ); out.write( (char*) &(header.yOrigin), 2 ); out.write( (char*) &(header.width), 2 ); out.write( (char*) &(header.height), 2 ); out.write( (char*) &(header.bitsPerPixel), 1 ); out.write( (char*) &(header.imageSpecification), 1 ); // convert pixel data from floating point vector pixelData( w*h*3 ); for( int i = 0; i < w*h*3; i++ ) { pixelData[i] = (unsigned char)( pixels[i] * 255. ); } // write pixel data out.write( (char*) &pixelData[0], w*h*3 ); } void Image :: clamp( int& x, int& y ) const // clamps coordinates to range [0,w-1] x [0,h-1] { x = max( 0, min( w-1, x )); y = max( 0, min( h-1, y )); } } ================================================ FILE: BaseCode/src/LinearContext.cpp ================================================ #include "LinearContext.h" namespace DDG { // global context for linear solvers LinearContext context; LinearContext :: LinearContext( void ) // constructor { cholmod_l_start( &context ); } LinearContext :: ~LinearContext( void ) // destructor { cholmod_l_finish( &context ); } LinearContext :: operator cholmod_common*( void ) // allows LinearContext to be treated as a cholmod_common* { return &context; } } ================================================ FILE: BaseCode/src/LinearEquation.cpp ================================================ #include "LinearEquation.h" namespace DDG { LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ) // constructs a linear equation with the specified left- and right-hand side { LinearEquation eqn; eqn.lhs = lhs; eqn.rhs = rhs; return eqn; } } ================================================ FILE: BaseCode/src/LinearPolynomial.cpp ================================================ #include using namespace std; #include "LinearPolynomial.h" #include "Types.h" namespace DDG { LinearPolynomial :: LinearPolynomial( void ) : constantTerm( 0. ) {} LinearPolynomial :: LinearPolynomial( double c ) { *this = c; } LinearPolynomial :: LinearPolynomial( Variable& v ) { *this = v; } const LinearPolynomial& LinearPolynomial :: operator=( double c ) { linearTerms.clear(); constantTerm = c; return *this; } const LinearPolynomial& LinearPolynomial :: operator=( Variable& v ) { linearTerms.clear(); linearTerms[ &v ] = 1.; constantTerm = 0.; return *this; } void LinearPolynomial::operator+=( double c ) { constantTerm += c; } void LinearPolynomial::operator-=( double c ) { constantTerm -= c; } void LinearPolynomial::operator*=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second *= c; } constantTerm *= c; } void LinearPolynomial::operator/=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second /= c; } constantTerm /= c; } void LinearPolynomial::operator+=( Variable& v ) { LinearPolynomial p( v ); *this += p; } void LinearPolynomial::operator-=( Variable& v ) { LinearPolynomial p( v ); *this -= p; } void LinearPolynomial::operator+=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second += i->second; } else { linearTerms[i->first] = i->second; } } constantTerm += e.constantTerm; } void LinearPolynomial::operator-=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second -= i->second; } else { linearTerms[i->first] = -i->second; } } constantTerm -= e.constantTerm; } LinearPolynomial LinearPolynomial::operator-( void ) const { LinearPolynomial p = *this; for( TermIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { i->second = -i->second; } p.constantTerm = -p.constantTerm; return p; } double LinearPolynomial::evaluate( void ) const { double value = constantTerm; for( TermCIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { value += i->second * i->first->value; } return value; } LinearPolynomial operator+( double c, Variable& v ) { return c + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, double c ) { return LinearPolynomial(v) + c; } LinearPolynomial operator-( double c, Variable& v ) { return c - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, double c ) { return LinearPolynomial(v) - c; } LinearPolynomial operator*( double c, Variable& v ) { return LinearPolynomial(v) * c; } LinearPolynomial operator*( Variable& v, double c ) { return LinearPolynomial(v) * c; } LinearPolynomial operator/( Variable& v, double c ) { return LinearPolynomial(v) / c; } LinearPolynomial operator+( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) + LinearPolynomial(v2); } LinearPolynomial operator-( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) - LinearPolynomial(v2); } LinearPolynomial operator+( const LinearPolynomial& p, double c ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator+( double c, const LinearPolynomial& p ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, double c ) { LinearPolynomial difference = p; difference -= c; return difference; } LinearPolynomial operator-( double c, const LinearPolynomial& p ) { LinearPolynomial difference = -p; difference += c; return difference; } LinearPolynomial operator*( const LinearPolynomial& p, double c ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator*( double c, const LinearPolynomial& p ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator/( const LinearPolynomial& p, double c ) { LinearPolynomial quotient = p; quotient /= c; return quotient; } LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ) { return p + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) + p; } LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ) { return p - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) - p; } LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial sum = p; sum += q; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial difference = p; difference -= q; return difference; } ostream& operator<<( ostream& os, const LinearPolynomial& p ) { for( TermCIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { os << i->second << "*" << i->first->name << " + "; } os << p.constantTerm; return os; } } ================================================ FILE: BaseCode/src/LinearSystem.cpp ================================================ #include using namespace std; #include #include "LinearSystem.h" #include "LinearContext.h" #include "Types.h" namespace DDG { extern LinearContext context; void LinearSystem::clear( void ) // removes all equations from the system { equations.clear(); } void LinearSystem::push_back( const LinearEquation& e ) // appends the equation e to the sytem { equations.push_back( e ); } void LinearSystem::solve( void ) // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution { convertEquations(); indexVariables(); buildSparseMatrix(); buildRightHandSide(); computeSolution(); } void LinearSystem::convertEquations( void ) // converts each equation to its polynomial representation { currentEquations.clear(); for( EqnIter eqn = equations.begin(); eqn != equations.end(); eqn ++ ) { // move right-hand side to left-hand side LinearPolynomial p = eqn->lhs - eqn->rhs; // convert fixed variables to constants LinearPolynomial q( p.constantTerm ); for( TermIter t = p.linearTerms.begin(); t != p.linearTerms.end(); t ++ ) { const double& coefficient( t->second ); Variable& variable( *(t->first) ); // skip zeros if( coefficient == 0. ) continue; if( t->first->fixed ) { q += coefficient * variable.value; } else { q += coefficient * variable; } } if( q.linearTerms.size() > 0 ) { currentEquations.push_back( q ); } } nEquations = currentEquations.size(); } void LinearSystem::indexVariables( void ) // assign a unique index to each variable remaining in one of the polynomials { index.clear(); nVariables = 0; for( PolyCIter p = currentEquations.begin(); p != currentEquations.end(); p ++ ) { for( TermCIter t = p->linearTerms.begin(); t != p->linearTerms.end(); t ++ ) { IndexIter j = index.find( t->first ); // if we haven't seen this variable // before, assign it a unique index if( j == index.end() ) { index[ t->first ] = nVariables; nVariables++; } } } } void LinearSystem::buildSparseMatrix( void ) // build the sparse matrix representation of our current system { A = SparseMatrix( nEquations, nVariables ); for( int i = 0; i < nEquations; i++ ) { for( TermCIter t = currentEquations[i].linearTerms.begin(); t != currentEquations[i].linearTerms.end(); t ++ ) { int j = index[ t->first ]; A(i,j) = t->second; } } } void LinearSystem::buildRightHandSide( void ) // build the data vector for our current system { b = DenseMatrix( nEquations, 1 ); for( int i = 0; i < nEquations; i++ ) { b(i) = -currentEquations[i].constantTerm; } } void LinearSystem::computeSolution( void ) { // solve linear system Ax=b x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); // put solution values in variables for( IndexIter i = index.begin(); i != index.end(); i ++ ) { i->first->value = x( i->second ); } } } ================================================ FILE: BaseCode/src/Mesh.cpp ================================================ #include #include #include "Mesh.h" #include "MeshIO.h" #include "DiscreteExteriorCalculus.h" using namespace std; namespace DDG { Mesh :: Mesh( void ) {} Mesh :: Mesh( const Mesh& mesh ) { *this = mesh; } class HalfEdgeIterCompare { public: bool operator()( const HalfEdgeIter& i, const HalfEdgeIter& j ) const { return &*i < &*j; } }; class HalfEdgeCIterCompare { public: bool operator()( const HalfEdgeCIter& i, const HalfEdgeCIter& j ) const { return &*i < &*j; } }; class VertexIterCompare { public: bool operator()( const VertexIter& i, const VertexIter& j ) const { return &*i < &*j; } }; class VertexCIterCompare { public: bool operator()( const VertexCIter& i, const VertexCIter& j ) const { return &*i < &*j; } }; class FaceIterCompare { public: bool operator()( const FaceIter& i, const FaceIter& j ) const { return &*i < &*j; } }; class FaceCIterCompare { public: bool operator()( const FaceCIter& i, const FaceCIter& j ) const { return &*i < &*j; } }; class EdgeIterCompare { public: bool operator()( const EdgeIter& i, const EdgeIter& j ) const { return &*i < &*j; } }; class EdgeCIterCompare { public: bool operator()( const EdgeCIter& i, const EdgeCIter& j ) const { return &*i < &*j; } }; const Mesh& Mesh :: operator=( const Mesh& mesh ) { map< HalfEdgeCIter, HalfEdgeIter, HalfEdgeCIterCompare > halfedgeOldToNew; map< VertexCIter, VertexIter, VertexCIterCompare > vertexOldToNew; map< EdgeCIter, EdgeIter, EdgeCIterCompare > edgeOldToNew; map< FaceCIter, FaceIter, FaceCIterCompare > faceOldToNew; // copy geometry from the original mesh and create a // map from pointers in the original mesh to // those in the new mesh halfedges.clear(); for( HalfEdgeCIter he = mesh.halfedges.begin(); he != mesh.halfedges.end(); he++ ) halfedgeOldToNew[ he ] = halfedges.insert( halfedges.end(), *he ); vertices.clear(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) vertexOldToNew[ v ] = vertices.insert( vertices.end(), *v ); edges.clear(); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e++ ) edgeOldToNew[ e ] = edges.insert( edges.end(), *e ); faces.clear(); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) faceOldToNew[ f ] = faces.insert( faces.end(), *f ); // "search and replace" old pointers with new ones for( HalfEdgeIter he = halfedges.begin(); he != halfedges.end(); he++ ) { he->next = halfedgeOldToNew[ he->next ]; he->flip = halfedgeOldToNew[ he->flip ]; he->vertex = vertexOldToNew[ he->vertex ]; he->edge = edgeOldToNew[ he->edge ]; he->face = faceOldToNew[ he->face ]; } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) v->he = halfedgeOldToNew[ v->he ]; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) e->he = halfedgeOldToNew[ e->he ]; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) f->he = halfedgeOldToNew[ f->he ]; return *this; } int Mesh::read( const string& filename ) { inputFilename = filename; ifstream in( filename.c_str() ); if( !in.is_open() ) { cerr << "Error reading from mesh file " << filename << endl; return 1; } int rval; if( !( rval = MeshIO::read( in, *this ))) { indexElements(); normalize(); } return rval; } int Mesh::write( const string& filename ) const // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error { ofstream out( filename.c_str() ); if( !out.is_open() ) { cerr << "Error writing to mesh file " << filename << endl; return 1; } MeshIO::write( out, *this ); return 0; } bool Mesh::reload( void ) { return read( inputFilename ); } void Mesh::normalize( void ) { // compute center of mass Vector c( 0., 0., 0. ); for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { c += v->position; } c /= (double) vertices.size(); // translate to origin for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position -= c; } // rescale such that the mesh sits inside the unit ball double rMax = 0.; for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { rMax = max( rMax, v->position.norm() ); } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position /= rMax; } } void Mesh::indexElements( void ) { int nV = 0; for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->index = nV; nV++; } int nE = 0; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) { e->index = nE; nE++; } int nF = 0; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) { f->index = nF; nF++; } } double Mesh::area( void ) const { double sum = 0.0; for( FaceCIter f = faces.begin(); f != faces.end(); f ++ ) { sum += f->area(); } return sum; } double Mesh::meanEdgeLength( void ) const { double sum = 0; for( EdgeCIter e = edges.begin(); e != edges.end(); e ++) { VertexIter v0 = e->he->vertex; VertexIter v1 = e->he->flip->vertex; sum += (v0->position - v1->position).norm(); } return sum / edges.size(); } } ================================================ FILE: BaseCode/src/MeshIO.cpp ================================================ #include #include #include #include #include "MeshIO.h" #include "Mesh.h" using namespace std; namespace DDG { class Index { public: Index( void ) {} Index( int p, int t, int n ) : position( p ), texcoord( t ), normal( n ) {} bool operator<( const Index& i ) const { if( position < i.position ) return true; if( position > i.position ) return false; if( texcoord < i.texcoord ) return true; if( texcoord > i.texcoord ) return false; if( normal < i.normal ) return true; if( normal > i.normal ) return false; return false; } int position; int texcoord; int normal; }; class MeshData { public: std::vector positions; std::vector texcoords; std::vector normals; std::vector< std::vector< Index > > indices; }; int MeshIO :: read( istream& in, Mesh& mesh ) // reads a mesh from a valid, open input stream in { MeshData data; if( readMeshData( in, data )) { return 1; } if( buildMesh( data, mesh )) { return 1; } return 0; } void MeshIO :: write( ostream& out, const Mesh& mesh ) // writes a mesh to a valid, open output stream out { int currentIndex = 1; map vertexIndex; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) { out << "v " << v->position.x << " " << v->position.y << " " << v->position.z << endl; vertexIndex[ v ] = currentIndex; currentIndex++; } for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeIter he = f->he; for( int j = 0; j < 3; j++ ) { out << "vt " << he->texcoord.x << " " << he->texcoord.y << endl; he = he->next; } } for( size_t i = 0; i < mesh.faces.size(); i++ ) { const Face& f( mesh.faces[i] ); HalfEdgeIter he = f.he; out << "f "; int j = 0; do { out << vertexIndex[ he->vertex ] << "/" << 1+(i*3+j) << " "; he = he->next; j++; } while( he != f.he ); out << endl; } } int MeshIO :: readMeshData( istream& in, MeshData& data ) { string line; while( getline( in, line )) { stringstream ss( line ); string token; ss >> token; if( token == "v" ) { readPosition( ss, data ); continue; } // vertex if( token == "vt" ) { readTexCoord( ss, data ); continue; } // texture coordinate if( token == "vn" ) { readNormal ( ss, data ); continue; } // vertex normal if( token == "f" ) { readFace ( ss, data ); continue; } // face if( token[0] == '#' ) continue; // comment if( token == "o" ) continue; // object name if( token == "g" ) continue; // group name if( token == "s" ) continue; // smoothing group if( token == "mtllib" ) continue; // material library if( token == "usemtl" ) continue; // material if( token == "" ) continue; // empty string cerr << "Error: does not appear to be a valid Wavefront OBJ file!" << endl; cerr << "(Offending line: " << line << ")" << endl; return 1; } return 0; } void MeshIO :: preallocateMeshElements( const MeshData& data, Mesh& mesh ) { // count the number of edges set< pair > edges; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { for( unsigned int I = 0; I < f->size(); I++ ) { int J = (I+1) % f->size(); int i = (*f)[I].position; int j = (*f)[J].position; if( i > j ) swap( i, j ); edges.insert( pair( i, j )); } } int nV = data.positions.size(); int nE = edges.size(); int nF = data.indices.size(); int nHE = 2*nE; int chi = nV - nE + nF; int nB = max( 0, 2 - chi ); // (conservative approximation of number of boundary cycles) mesh.halfedges.clear(); mesh.vertices.clear(); mesh.edges.clear(); mesh.faces.clear(); mesh.boundaries.clear(); mesh.halfedges.reserve( nHE ); mesh.vertices.reserve( nV ); mesh.edges.reserve( nE ); mesh.faces.reserve( nF ); mesh.boundaries.reserve( nB ); } extern vector isolated; // all isolated vertices point to isolated.begin() int MeshIO :: buildMesh( const MeshData& data, Mesh& mesh ) { map< pair< int, int >, int > edgeCount; map< pair< int, int >, HalfEdgeIter > existingHalfEdges; map< int, VertexIter > indexToVertex; map< HalfEdgeIter, bool > hasFlipEdge; preallocateMeshElements( data, mesh ); // allocate a vertex for each position in the data and construct // a map from vertex indices to vertex pointers for( unsigned int i = 0; i < data.positions.size(); i++ ) { VertexIter newVertex = mesh.vertices.insert( mesh.vertices.end(), Vertex() ); newVertex->position = data.positions[ i ]; newVertex->he = isolated.begin(); indexToVertex[ i ] = newVertex; } // insert each face into the mesh int faceIndex = 0; bool degenerateFaces = false; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { int N = f->size(); // print an error if the face is degenerate if( N < 3 ) { cerr << "Error: face " << faceIndex << " is degenerate (fewer than three vertices)!" << endl; degenerateFaces = true; continue; } // create a new face FaceIter newFace = mesh.faces.insert( mesh.faces.end(), Face()); // create a new half edge for each edge of the current face vector< HalfEdgeIter > hes( N ); for( int i = 0; i < N; i++ ) { hes[ i ] = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); } // initialize these new halfedges for( int i = 0; i < N; i++ ) { // the current halfedge goes from vertex a to vertex b int a = (*f)[ i ].position; int b = (*f)[ (i+1) % N ].position; // set current halfedge's attributes hes[ i ]->next = hes[ (i+1) % N ]; hes[ i ]->vertex = indexToVertex[ a ]; int t = (*f)[i].texcoord; if( t >= 0 ) hes[ i ]->texcoord = data.texcoords[ t ]; else hes[ i ]->texcoord = Vector( 0., 0., 0. ); hes[ i ]->onBoundary = false; // keep track of which halfedges have flip edges defined (for detecting boundaries) hasFlipEdge[ hes[ i ]] = false; // point vertex a at the current halfedge indexToVertex[ a ]->he = hes[ i ]; // point the new face and this half edge to each-other hes[ i ]->face = newFace; newFace->he = hes[ i ]; // if we've created an edge between a and b in the past, it is the // flip edge of the current halfedge if( a > b ) swap( a, b ); if( existingHalfEdges.find( pair( a, b )) != existingHalfEdges.end()) { hes[ i ]->flip = existingHalfEdges[ pair( a, b ) ]; hes[ i ]->flip->flip = hes[ i ]; hes[ i ]->edge = hes[ i ]->flip->edge; hasFlipEdge[ hes[ i ]] = true; hasFlipEdge[ hes[ i ]->flip ] = true; } else // otherwise, create an edge connected to the current halfedge { hes[ i ]->edge = mesh.edges.insert( mesh.edges.end(), Edge()); hes[ i ]->edge->he = hes[i]; edgeCount[ pair( a, b ) ] = 0; } // record the fact that we've created a halfedge from a to b existingHalfEdges[ pair( a, b ) ] = hes[ i ]; // check for nonmanifold edges edgeCount[ pair( a, b ) ]++; if( edgeCount[ pair( a, b ) ] > 2 ) { cerr << "Error: edge (" << a << ", " << b << ") is nonmanifold (more than two faces sharing a single edge)!" << endl; return 1; } } faceIndex++; } // give up now if there were degenerate faces if( degenerateFaces ) { return 1; } // insert extra faces for each boundary cycle for( HalfEdgeIter currentHE = mesh.halfedges.begin(); currentHE != mesh.halfedges.end(); currentHE ++ ) { // if we find a halfedge with no flip edge defined, create // a new face and link it to the corresponding boundary cycle if( !hasFlipEdge[ currentHE ] ) { // create a new face FaceIter newBoundary = mesh.boundaries.insert( mesh.boundaries.end(), Face()); // walk along this boundary cycle vector boundaryCycle; HalfEdgeIter he = currentHE; do { // create a new halfedge on the boundary face HalfEdgeIter newHE = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); // mark only the halfedge on the boundary face as being on the boundary newHE->onBoundary = true; // link the current halfedge in the cycle to its new flip edge he->flip = newHE; // grab the next halfedge along the boundary by finding // the next halfedge around the current vertex that doesn't // have a flip edge defined HalfEdgeIter nextHE = he->next; while( hasFlipEdge[ nextHE ] ) { nextHE = nextHE->flip->next; } // set attributes for the flip edge (we'll set ->next below) newHE->flip = he; newHE->vertex = nextHE->vertex; newHE->edge = he->edge; newHE->face = newBoundary; newHE->texcoord = nextHE->texcoord; // point the new face to this half edge newBoundary->he = newHE; // keep track of all the new halfedges in the boundary cycle boundaryCycle.push_back( newHE ); // continue to walk along the cycle he = nextHE; } while( he != currentHE ); // link together the cycle of boundary halfedges unsigned int N = boundaryCycle.size(); for( unsigned int i = 0; i < N; i++ ) { boundaryCycle[ i ]->next = boundaryCycle[ (i+N-1)%N ]; hasFlipEdge[ boundaryCycle[i] ] = true; hasFlipEdge[ boundaryCycle[i]->flip ] = true; } } } // print a warning if the input has any non-terminal defects checkIsolatedVertices( mesh ); checkNonManifoldVertices( mesh ); return 0; } void MeshIO :: readPosition( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.positions.push_back( Vector( x, y, z )); } void MeshIO :: readTexCoord( stringstream& ss, MeshData& data ) { double u, v; ss >> u >> v; data.texcoords.push_back( Vector( u, v, 0. )); } void MeshIO :: readNormal( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.normals.push_back( Vector( x, y, z )); } void MeshIO :: readFace( stringstream& ss, MeshData &data ) { vector faceIndices; string token; while( ss >> token ) { faceIndices.push_back( parseFaceIndex( token )); } data.indices.push_back( faceIndices ); } Index MeshIO :: parseFaceIndex( const string& token ) { // parse indices of the form // // p/[t]/[n] // // where p is an index into positions, t is an index into // texcoords, n is an index into normals, and [.] indicates // that an index is optional stringstream in( token ); string indexstring; int indices[3] = { -1, -1, -1 }; int i = 0; while( getline( in, indexstring, '/' )) { stringstream ss( indexstring ); ss >> indices[i++]; } // decrement since indices in OBJ files are 1-based return Index( indices[0]-1, indices[1]-1, indices[2]-1 ); } void MeshIO :: checkIsolatedVertices( const Mesh& mesh ) { // print a warning if the mesh has any isolated vertices int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { cerr << "Warning: vertex " << vertexIndex << " is isolated (not contained in any face)." << endl; } vertexIndex++; } } void MeshIO :: checkNonManifoldVertices( const Mesh& mesh ) { map nIncidentFaces; for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } for( FaceCIter f = mesh.boundaries.begin(); f != mesh.boundaries.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( nIncidentFaces[v] != v->valence() ) { cerr << "Warning: vertex " << vertexIndex << " is nonmanifold." << endl; } vertexIndex++; } } } ================================================ FILE: BaseCode/src/Quaternion.cpp ================================================ #include #include using namespace std; #include "Quaternion.h" namespace DDG { // CONSTRUCTORS ---------------------------------------------------------- Quaternion :: Quaternion( void ) // initializes all components to zero : s( 0. ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Quaternion& q ) // initializes from existing quaternion : s( q.s ), v( q.v ) {} Quaternion :: Quaternion( double s_, double vi, double vj, double vk ) // initializes with specified double (s) and imaginary (v) components : s( s_ ), v( vi, vj, vk ) {} Quaternion :: Quaternion( double s_, const Vector& v_ ) // initializes with specified double(s) and imaginary (v) components : s( s_ ), v( v_ ) {} Quaternion :: Quaternion( double s_ ) // initializes purely real quaternion with specified real (s) component (imaginary part is zero) : s( s_ ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Vector& v_ ) // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) : s( 0. ), v( v_ ) {} Quaternion :: Quaternion( const Complex& z ) // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k : s( z.re ), v( z.im, 0., 0. ) {} // ASSIGNMENT OPERATORS -------------------------------------------------- const Quaternion& Quaternion :: operator=( double _s ) // assigns a purely real quaternion with real value s { s = _s; v = Vector( 0., 0., 0. ); return *this; } const Quaternion& Quaternion :: operator=( const Vector& _v ) // assigns a purely real quaternion with imaginary value v { s = 0.; v = _v; return *this; } // ACCESSORS ------------------------------------------------------------- double& Quaternion::operator[]( int index ) // returns reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } const double& Quaternion::operator[]( int index ) const // returns const reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } void Quaternion::toMatrix( double Q[4][4] ) const // returns 4x4 matrix representation { Q[0][0] = s; Q[0][1] = -v.x; Q[0][2] = -v.y; Q[0][3] = -v.z; Q[1][0] = v.x; Q[1][1] = s; Q[1][2] = -v.z; Q[1][3] = v.y; Q[2][0] = v.y; Q[2][1] = v.z; Q[2][2] = s; Q[2][3] = -v.x; Q[3][0] = v.z; Q[3][1] = -v.y; Q[3][2] = v.x; Q[3][3] = s; } double& Quaternion::re( void ) // returns reference to double part { return s; } const double& Quaternion::re( void ) const // returns const reference to double part { return s; } Vector& Quaternion::im( void ) // returns reference to imaginary part { return v; } const Vector& Quaternion::im( void ) const // returns const reference to imaginary part { return v; } // VECTOR SPACE OPERATIONS ----------------------------------------------- Quaternion Quaternion::operator+( const Quaternion& q ) const // addition { return Quaternion( s+q.s, v+q.v ); } Quaternion Quaternion::operator-( const Quaternion& q ) const // subtraction { return Quaternion( s-q.s, v-q.v ); } Quaternion Quaternion::operator-( void ) const // negation { return Quaternion( -s, -v ); } Quaternion Quaternion::operator*( double c ) const // scalar multiplication { return Quaternion( s*c, v*c ); } Quaternion operator*( double c, const Quaternion& q ) // scalar multiplication { return q*c; } Quaternion Quaternion::operator/( double c ) const // scalar division { return Quaternion( s/c, v/c ); } void Quaternion::operator+=( const Quaternion& q ) // addition / assignment { s += q.s; v += q.v; } void Quaternion::operator+=( double c ) // addition / assignment of pure real { s += c; } void Quaternion::operator-=( const Quaternion& q ) // subtraction / assignment { s -= q.s; v -= q.v; } void Quaternion::operator-=( double c ) // subtraction / assignment of pure real { s -= c; } void Quaternion::operator*=( double c ) // scalar multiplication / assignment { s *= c; v *= c; } void Quaternion::operator/=( double c ) // scalar division / assignment { s /= c; v /= c; } // ALGEBRAIC OPERATIONS -------------------------------------------------- Quaternion Quaternion::operator*( const Quaternion& q ) const // Hamilton product { const double& s1( s ); const double& s2( q.s ); const Vector& v1( v ); const Vector& v2( q.v ); return Quaternion( s1*s2 - dot(v1,v2), s1*v2 + s2*v1 + cross(v1,v2) ); } void Quaternion::operator*=( const Quaternion& q ) // Hamilton product / assignment { *this = ( *this * q ); } Quaternion Quaternion::conj( void ) const // conjugation { return Quaternion( s, -v ); } Quaternion Quaternion::inv( void ) const { return ( this->conj() ) / this->norm2(); } // NORMS ----------------------------------------------------------------- double Quaternion::norm( void ) const // returns Euclidean length { return sqrt( s*s + v.x*v.x + v.y*v.y + v.z*v.z ); } double Quaternion::norm2( void ) const // returns Euclidean length squared { return s*s + dot(v,v); } Quaternion Quaternion::unit( void ) const // returns unit quaternion { return *this / norm(); } void Quaternion::normalize( void ) // divides by Euclidean length { *this /= norm(); } // GEOMETRIC OPERATIONS -------------------------------------------------- Quaternion slerp( const Quaternion& q0, const Quaternion& q1, double t ) // spherical-linear interpolation { // interpolate length double m0 = q0.norm(); double m1 = q1.norm(); double m = (1-t)*m0 + t*m1; // interpolate direction Quaternion p0 = q0 / m0; Quaternion p1 = q1 / m1; double theta = acos(( p0.conj()*p1 ).re() ); Quaternion p = ( sin((1-t)*theta)*p0 + sin(t*theta)*p1 )/sin(theta); return m*p; } // I/O ------------------------------------------------------------------------- std::ostream& operator<<( std::ostream& os, const Quaternion& q ) // prints components { os << "( " << q.re() << ", " << q.im() << " )"; return os; } } ================================================ FILE: BaseCode/src/Real.cpp ================================================ #include "Real.h" #include namespace DDG { Real :: Real( double x ) // constructs real number with value x : value( x ) {} Real :: operator double( void ) const // type cast to double { return value; } void Real :: operator+=( double x ) // increment { value += x; } void Real :: operator-=( double x ) // decrement { value -= x; } void Real :: operator*=( double x ) // multiply { value *= x; } void Real :: operator/=( double x ) // divide { value /= x; } Real Real :: conj( void ) const // simply returns the value (for compatibility w/ complex numbers) { return value; } Real Real :: inv( void ) const // returns inverse { return 1. / value; } double Real :: norm( void ) const // returns norm { return fabs( value ); } double Real :: norm2( void ) const // returns norm squared { return value * value; } Real Real :: unit( void ) const // returns number with unit norm and same sign { return value / norm(); } } ================================================ FILE: BaseCode/src/Shader.cpp ================================================ #include "Shader.h" #include #include using namespace std; namespace DDG { Shader::Shader( void ) // constructor -- shader is initially invalid : vertexShader( 0 ), fragmentShader( 0 ), geometryShader( 0 ), program( 0 ), linked( false ) {} Shader::~Shader( void ) { if( program ) glDeleteProgram( program ); if( vertexShader ) glDeleteShader( vertexShader ); if( fragmentShader ) glDeleteShader( fragmentShader ); if( geometryShader ) glDeleteShader( geometryShader ); } void Shader::loadVertex( const char* filename ) { load( GL_VERTEX_SHADER, filename, vertexShader ); } void Shader::loadFragment( const char* filename ) { load( GL_FRAGMENT_SHADER, filename, fragmentShader ); } void Shader::loadGeometry( const char* filename ) { #ifdef GL_GEOMETRY_SHADER_EXT load( GL_GEOMETRY_SHADER_EXT, filename, geometryShader ); #else cerr << "Error: geometry shaders not supported!" << endl; #endif } void Shader::enable( void ) { if( !linked ) { glLinkProgram( program ); linked = true; } glUseProgram( program ); } void Shader::disable( void ) const { glUseProgram( 0 ); } Shader::operator GLuint( void ) const { return program; } void Shader::load( GLenum shaderType, const char* filename, GLuint& shader ) // read vertex shader from GLSL source file, compile, and attach to program { string source; if( !readSource( filename, source )) { return; } if( program == 0 ) { program = glCreateProgram(); } if( shader != 0 ) { glDetachShader( program, shader ); } shader = glCreateShader( shaderType ); const char* source_c_str = source.c_str(); glShaderSource( shader, 1, &(source_c_str), NULL ); glCompileShader( shader ); GLint compileStatus; glGetShaderiv( shader, GL_COMPILE_STATUS, &compileStatus ); if( compileStatus == GL_TRUE ) { glAttachShader( program, shader ); linked = false; } else { GLsizei maxLength = 0; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength ); if( maxLength > 0 ) { GLchar* infoLog = new char[ maxLength ]; GLsizei length; glGetShaderInfoLog( shader, maxLength, &length, infoLog ); cerr << "GLSL Error: " << infoLog << endl; delete[] infoLog; } } } bool Shader::readSource( const char* filename, std::string& source ) // reads GLSL source file into a string { source = ""; ifstream in( filename ); if( !in.is_open() ) { cerr << "Error: could not open shader file "; cerr << filename; cerr << " for input!" << endl; return false; } string line; while( getline( in, line )) { source += line; } return true; } } ================================================ FILE: BaseCode/src/SparseMatrix.cpp ================================================ #include "SparseMatrix.h" namespace DDG { template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = pr[k]; } } return *this; } template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = Complex( pr[k*2+0], pr[k*2+1] ); } } return *this; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ) { SparseMatrix A( m*4, n*4 ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; const Quaternion& q( e->second ); A(i*4+0,j*4+0) = q[0]; A(i*4+0,j*4+1) = -q[1]; A(i*4+0,j*4+2) = -q[2]; A(i*4+0,j*4+3) = -q[3]; A(i*4+1,j*4+0) = q[1]; A(i*4+1,j*4+1) = q[0]; A(i*4+1,j*4+2) = -q[3]; A(i*4+1,j*4+3) = q[2]; A(i*4+2,j*4+0) = q[2]; A(i*4+2,j*4+1) = q[3]; A(i*4+2,j*4+2) = q[0]; A(i*4+2,j*4+3) = -q[1]; A(i*4+3,j*4+0) = q[3]; A(i*4+3,j*4+1) = -q[2]; A(i*4+3,j*4+2) = q[1]; A(i*4+3,j*4+3) = q[0]; } if( cData != NULL ) { cholmod_l_free_sparse( &cData, context ); } cData = cholmod_l_copy_sparse( A.to_cholmod(), context ); return cData; } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_COMPLEX, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m*4, n*4, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i] = e->second; } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i*2+0] = e->second.re; pr[i*2+1] = e->second.im; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR< complex >( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (complex)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (quaternion)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4]/4 << "\n"; } template <> void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_zl_symbolic( n, n, Ap, Ai, Ax, NULL, &Symbolic, NULL, NULL ); umfpack_zl_numeric( Ap, Ai, Ax, NULL, Symbolic, &Numeric, NULL, NULL ); umfpack_zl_solve( UMFPACK_A, Ap, Ai, Ax, NULL, (double*) &x(0), NULL, (double*) &b(0), NULL, Numeric, NULL, NULL ); umfpack_zl_free_symbolic( &Symbolic ); umfpack_zl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } } ================================================ FILE: BaseCode/src/SparseMatrix.inl ================================================ #include #include #include #include #include using namespace std; #include #include #include "Real.h" #include "Complex.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "LinearContext.h" #include "Utility.h" namespace DDG { extern LinearContext context; const int maxEigIter = 20; // number of iterations used to solve eigenvalue problems template SparseMatrix :: SparseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) {} template SparseMatrix :: SparseMatrix( const SparseMatrix& B ) // copy constructor : cData( NULL ) { *this = B; } template SparseMatrix :: ~SparseMatrix( void ) // destructor { if( cData ) { cholmod_l_free_sparse( &cData, context ); } } template const SparseMatrix& SparseMatrix :: operator=( const SparseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template SparseMatrix SparseMatrix :: transpose( void ) const { SparseMatrix AT( n, m ); for( const_iterator e = begin(); e != end(); e++ ) { int i = e->first.second; int j = e->first.first; T Aij = e->second; AT(j,i) = Aij.conj(); } return AT; } template SparseMatrix SparseMatrix :: operator*( const SparseMatrix& B ) const // returns product of this matrix with sparse B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // collect nonzeros in each row vector< vector< int > > Bcol( B.nRows() ); vector< vector< T > > Bval( B.nRows() ); for( const_iterator e = B.begin(); e != B.end(); e ++ ) { int row = e->first.second; int col = e->first.first; T val = e->second; Bcol[ row ].push_back( col ); Bval[ row ].push_back( val ); } // multiply C = A*B SparseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( size_t n = 0; n < Bcol[j].size(); n++ ) { int k = Bcol[j][n]; C( i, k ) += e->second * Bval[j][n]; } } return C; } template DenseMatrix SparseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with dense B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // multiply C = A*B DenseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( int k = 0; k < B.nColumns(); k++ ) { C( i, k ) += e->second * B( j, k ); } } return C; } template void SparseMatrix :: operator*=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second *= c; } } template void SparseMatrix :: operator/=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second /= c; } } template void SparseMatrix :: operator+=( const SparseMatrix& B ) // adds B to this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) += Bij; } } template void SparseMatrix :: operator-=( const SparseMatrix& B ) // subtracts B from this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) -= Bij; } } template SparseMatrix SparseMatrix :: operator+( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C += B; return C; } template SparseMatrix SparseMatrix :: operator-( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C -= B; return C; } template SparseMatrix operator*( const T& c, const SparseMatrix& A ) { SparseMatrix cA = A; for( typename SparseMatrix::iterator e = cA.begin(); e != cA.end(); e++ ) { e->second = c * e->second; } return cA; } template SparseMatrix operator*( const SparseMatrix& A, const T& c ) { SparseMatrix Ac = A; Ac *= c; return Ac; } template SparseMatrix operator/( const SparseMatrix& A, T c ) { SparseMatrix Ac = A; Ac /= c; return Ac; } template void SparseMatrix :: resize( int m_, int n_ ) { m = m_; n = n_; data.clear(); } template int SparseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int SparseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int SparseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void SparseMatrix :: zero( const T& val ) // sets all nonzero elements val { for( iterator i = begin(); i != end(); i++ ) { i->second = val; } } template SparseMatrix SparseMatrix :: inverse( void ) const // returns inverse -- for diagonal matrices only { assert( m == n ); // matrix must be square const SparseMatrix& A( *this ); SparseMatrix Ainv( m, m ); for( const_iterator e = begin(); e != end(); e++ ) { int r = e->first.second; int c = e->first.first; assert( r == c ); // matrix must be diagonal Ainv( r, c ) = A( r, c ).inv(); } return Ainv; } template SparseMatrix SparseMatrix :: identity( int N ) { SparseMatrix I( N, N ); for( int i = 0; i < N; i++ ) { I( i, i ) = 1.; } return I; } template DenseMatrix SparseMatrix :: full( void ) const // converts to a dense matrix { const int maxSize = 1048576; if( m*n > maxSize ) { cerr << "Error: refusing to convert sparse to dense (too big!)" << "\n"; exit( 1 ); } const SparseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < m; j++ ) { B( i, j ) = A( i, j ); } return B; } template cholmod_sparse* SparseMatrix :: to_cholmod( void ) { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } allocateSparse(); // build compressed matrix (note that EntryMap stores entries in column-major order) double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; int i = 0; int j = -1; for( const_iterator e = begin(); e != end(); e ++ ) { int c = e->first.first; if( c != j ) { for( int k = j+1; k <= c; k++ ) { jc[k] = i; } j = c; } ir[i] = e->first.second; setEntry( e, i, pr ); i++; } for( int k = j+1; k <= n; k++ ) { jc[k] = i; } return cData; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ); template T& SparseMatrix :: operator()( int row, int col ) { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { data[ index ] = T( 0. ); } return data[ index ]; } template T SparseMatrix :: operator()( int row, int col ) const { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { return T( 0. ); } return entry->second; } template typename SparseMatrix::iterator SparseMatrix :: begin( void ) { return data.begin(); } template typename SparseMatrix::const_iterator SparseMatrix :: begin( void ) const { return data.begin(); } template typename SparseMatrix::iterator SparseMatrix :: end( void ) { return data.end(); } template typename SparseMatrix::const_iterator SparseMatrix :: end( void ) const { return data.end(); } template void SparseMatrix :: shift( double c ) // adds c times the identity matrix to this matrix { assert( m == n ); SparseMatrix& A( *this ); for( int i = 0; i < m; i++ ) { A( i, i ) += c; } } template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_dl_symbolic( n, n, Ap, Ai, Ax, &Symbolic, NULL, NULL ); umfpack_dl_numeric( Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL ); umfpack_dl_solve( UMFPACK_A, Ap, Ai, Ax, (double*) &x(0), (double*) &b(0), Numeric, NULL, NULL ); umfpack_dl_free_symbolic( &Symbolic ); umfpack_dl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; cholmod_factor* L = cholmod_l_analyze( Ac, context ); cholmod_l_factorize( Ac, L, context ); x = cholmod_l_solve( CHOLMOD_A, L, b.to_cholmod(), context ); if( L ) cholmod_l_free_factor( &L, context ); int t1 = clock(); cout << "[chol] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[chol] max residual: " << residual( A, x, b ) << "\n"; } template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ) // backsolves the prefactored positive definite sparse linear system LL'x = b { x = cholmod_l_solve( CHOLMOD_A, L.to_cholmod(), b.to_cholmod(), context ); } template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess { int t0 = clock(); for( int iter = 0; iter < maxEigIter; iter++ ) { solve( A, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess { // TODO use a symmetric matrix decomposition instead of QR int t0 = clock(); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= dot( e, B*e ).norm(); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; solve( A, x, x ); x -= dot( x, Be ).conj()*e; x /= dot( x, B*x ).norm(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); for( int iter = 0; iter < maxEigIter; iter++ ) { backsolvePositiveDefinite( L, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= sqrt( dot( e, B*e ).norm() ); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; backsolvePositiveDefinite( L, x, x ); x -= dot( x, Be ).conj()*e; x /= sqrt( dot( x, B*x ).norm() ); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ) // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess { int iter; int t0 = clock(); DenseMatrix ET = E.transpose(); SparseFactor L; L.build( A ); for( iter = 0; iter < maxEigIter; iter++ ) { x = B*x - E*(ET*x); backsolvePositiveDefinite( L, x, x ); x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, E, x ) << "\n"; } template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ) // returns the max residual of the linear problem A x = b relative to the largest entry of the solution { return ( A*x - b ).norm() / b.norm(); } template double residual( const SparseMatrix& A, const DenseMatrix& x ) // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, x ); return (A*x-lambda*x).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, x ); return (A*x-lambda*(B*x)).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, E, x ); return (A*x-lambda*(B*x-E*(E.transpose()*x))).norm() / x.norm(); } template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*x)(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x))(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns /<(B-EE^T)x,x> { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x-E*(E.transpose()*x)))(0).inv(); } template std::ostream& operator<<( std::ostream& os, const SparseMatrix& o) { os.precision( 3 ); for( typename SparseMatrix::const_iterator e = o.begin(); e != o.end(); e ++ ) { int row = e->first.second; int col = e->first.first; os << "( " << row << ", " << col << " ): " << e->second << "\n"; } return os; } template SparseFactor :: SparseFactor( void ) : L( NULL ) {} template SparseFactor :: ~SparseFactor( void ) { if( L ) { cholmod_l_free_factor( &L, context ); } } template void SparseFactor :: build( SparseMatrix& A ) { if( L ) { cholmod_l_free_factor( &L, context ); L = NULL; } int t0, t1; cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; t0 = clock(); L = cholmod_l_analyze( Ac, context ); t1 = clock(); cerr << "analyze: " << seconds(t0,t1) << "s" << endl; t0 = clock(); cholmod_l_factorize( Ac, L, context ); t1 = clock(); cerr << "factorize: " << seconds(t0,t1) << "s" << endl; } template bool SparseFactor :: valid( void ) const { if( L == NULL ) { return false; } return true; } template cholmod_factor* SparseFactor :: to_cholmod( void ) { return L; } } ================================================ FILE: BaseCode/src/Variable.cpp ================================================ #include "Variable.h" namespace DDG { Variable :: Variable( double value_, bool fixed_ ) // initialize a variable which has value zero and is not fixed by default : value( value_ ), fixed( fixed_ ) {} Variable :: Variable( std::string name_, double value_, bool fixed_ ) // initialize a named variable which has value zero and is not fixed by default : name( name_ ), value( value_ ), fixed( fixed_ ) {} double& Variable :: operator*( void ) // returns a reference to the numerical value { return value; } const double& Variable :: operator*( void ) const // returns a const reference to the numerical value { return value; } } ================================================ FILE: BaseCode/src/Vector.cpp ================================================ #include #include "Vector.h" namespace DDG { Vector :: Vector( void ) : x( 0. ), y( 0. ), z( 0. ) {} Vector :: Vector( double x0, double y0, double z0 ) : x( x0 ), y( y0 ), z( z0 ) {} Vector :: Vector( const Vector& v ) : x( v.x ), y( v.y ), z( v.z ) {} double& Vector :: operator[]( const int& index ) { return ( &x )[ index ]; } const double& Vector :: operator[]( const int& index ) const { return ( &x )[ index ]; } Vector Vector :: operator+( const Vector& v ) const { return Vector( x + v.x, y + v.y, z + v.z ); } Vector Vector :: operator-( const Vector& v ) const { return Vector( x - v.x, y - v.y, z - v.z ); } Vector Vector :: operator-( void ) const { return Vector( -x, -y, -z ); } Vector Vector :: operator*( const double& c ) const { return Vector( x*c, y*c, z*c ); } Vector operator*( const double& c, const Vector& v ) { return v*c; } Vector Vector :: operator/( const double& c ) const { return (*this) * ( 1./c ); } void Vector :: operator+=( const Vector& v ) { x += v.x; y += v.y; z += v.z; } void Vector :: operator-=( const Vector& v ) { x -= v.x; y -= v.y; z -= v.z; } void Vector :: operator*=( const double& c ) { x *= c; y *= c; z *= c; } void Vector :: operator/=( const double& c ) { (*this) *= ( 1./c ); } double Vector :: norm( void ) const { return sqrt( norm2()); } double Vector :: norm2( void ) const { return dot( *this, *this ); } void Vector :: normalize( void ) { (*this) /= norm(); } Vector Vector :: unit( void ) const { return (*this) / norm(); } Vector Vector :: abs( void ) const { return Vector( fabs( x ), fabs( y ), fabs( z ) ); } double dot( const Vector& u, const Vector& v ) { return u.x*v.x + u.y*v.y + u.z*v.z ; } Vector cross( const Vector& u, const Vector& v ) { return Vector( u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x ); } std::ostream& operator << (std::ostream& os, const Vector& o) { os << "[ " << o.x << " " << o.y << " " << o.z << " ]"; return os; } } ================================================ FILE: BaseCode/src/Vertex.cpp ================================================ #include using namespace std; #include "Vertex.h" #include "Mesh.h" #include "HalfEdge.h" namespace DDG { double Vertex::area( void ) const // returns the dual area associated with this vertex { double A = 0.; HalfEdgeCIter h = he; do { if (not h->onBoundary) A += h->face->area(); h = h->flip->next; } while( h != he ); return A / 3.; } Vector Vertex::normal( void ) const // returns the vertex normal { Vector N; HalfEdgeCIter h = he; do { if (not h->onBoundary) N += h->face->normal(); h = h->flip->next; } while( h != he ); return N.unit(); } vector isolated; // all isolated vertices point to isolated.begin() bool Vertex::isIsolated( void ) const // returns true if the vertex is not contained in any face or edge; false otherwise { return he == isolated.begin(); } int Vertex :: valence( void ) const // returns the number of incident faces { int n = 0; HalfEdgeCIter h = he; do { n++; h = h->flip->next; } while( h != he ); return n; } void Vertex :: toggleTag() { tag = !tag; } } ================================================ FILE: BaseCode/src/Viewer.cpp ================================================ #include #include #include #include #include #include #include using namespace std; #include "Viewer.h" #include "Image.h" #include "Application.h" namespace DDG { // declare static member variables Mesh Viewer::mesh; GLuint Viewer::surfaceDL = 0; int Viewer::windowSize[2] = { 512, 512 }; Camera Viewer::camera; Shader Viewer::shader; bool Viewer::renderWireframe = false; void Viewer :: init( void ) { restoreViewerState(); initGLUT(); setGL(); initGLSL(); updateDisplayList(); glutMainLoop(); } void Viewer :: initGLUT( void ) { int argc = 0; vector< vector > argv(1); // initialize window glutInitWindowSize( windowSize[0], windowSize[1] ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); glutInit( &argc, (char**)&argv ); glutCreateWindow( "DDG" ); // specify callbacks glutDisplayFunc ( Viewer::display ); glutIdleFunc ( Viewer::idle ); glutKeyboardFunc ( Viewer::keyboard ); glutSpecialFunc ( Viewer::special ); glutMouseFunc ( Viewer::mouse ); glutMotionFunc ( Viewer::motion ); // initialize menus int viewMenu = glutCreateMenu( Viewer::view ); glutSetMenu( viewMenu ); glutAddMenuEntry( "[f] Wireframe", menuWireframe ); glutAddMenuEntry( "[↑] Zoom In", menuZoomIn ); glutAddMenuEntry( "[↓] Zoom Out", menuZoomOut ); int mainMenu = glutCreateMenu( Viewer::menu ); glutSetMenu( mainMenu ); glutAddMenuEntry( "[space] Process Mesh", menuProcess ); glutAddMenuEntry( "[r] Reset Mesh", menuResetMesh ); glutAddMenuEntry( "[w] Write Mesh", menuWriteMesh ); glutAddMenuEntry( "[\\] Screenshot", menuScreenshot ); glutAddMenuEntry( "[esc] Exit", menuExit ); glutAddSubMenu( "View", viewMenu ); glutAttachMenu( GLUT_RIGHT_BUTTON ); } void Viewer :: initGLSL( void ) { shader.loadVertex( "shaders/vertex.glsl" ); shader.loadFragment( "shaders/fragment.glsl" ); } void Viewer :: menu( int value ) { switch( value ) { case( menuProcess ): mProcess(); break; case( menuResetMesh ): mResetMesh(); break; case( menuWriteMesh ): mWriteMesh(); break; case( menuScreenshot ): mScreenshot(); break; case( menuExit ): mExit(); break; default: break; } } void Viewer :: view( int value ) { switch( value ) { case( menuWireframe ): mWireframe(); break; case( menuZoomIn ): mZoomIn(); break; case( menuZoomOut ): mZoomOut(); break; default: break; } } void Viewer :: keyboard( unsigned char c, int x, int y ) { switch( c ) { case 'f': mWireframe(); break; case 'w': mWriteMesh(); break; case 'r': mResetMesh(); break; case '\\': mScreenshot(); break; case ' ': mProcess(); break; case 27: mExit(); break; default: break; } } void Viewer :: special( int i, int x, int y ) { switch( i ) { case GLUT_KEY_UP: camera.zoomIn(); break; case GLUT_KEY_DOWN: camera.zoomOut(); break; case 27: mExit(); break; default: break; } } void Viewer :: mouse( int button, int state, int x, int y ) { if( ( glutGetModifiers() & GLUT_ACTIVE_SHIFT) and state == GLUT_UP ) pickVertex(x, y); else camera.mouse( button, state, x, y ); } void Viewer :: motion( int x, int y ) { camera.motion( x, y ); } void Viewer :: idle( void ) { camera.idle(); glutPostRedisplay(); } void Viewer :: storeViewerState( void ) { ofstream out( ".viewer_state.txt" ); out << camera.rLast[0] << endl; out << camera.rLast[1] << endl; out << camera.rLast[2] << endl; out << camera.rLast[3] << endl; GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); out << view[2] << endl; out << view[3] << endl; } void Viewer :: restoreViewerState( void ) { ifstream in( ".viewer_state.txt" ); if( !in.is_open() ) return; in >> camera.rLast[0]; in >> camera.rLast[1]; in >> camera.rLast[2]; in >> camera.rLast[3]; in >> windowSize[0]; in >> windowSize[1]; } void Viewer :: mProcess( void ) { // TODO: call Application here! updateDisplayList(); } void Viewer :: mResetMesh( void ) { mesh.reload(); updateDisplayList(); } void Viewer :: mWriteMesh( void ) { mesh.write( "out.obj" ); } void Viewer :: mExit( void ) { storeViewerState(); exit( 0 ); } void Viewer :: mWireframe( void ) { renderWireframe = !renderWireframe; updateDisplayList(); } void Viewer :: mZoomIn( void ) { camera.zoomIn(); } void Viewer :: mZoomOut( void ) { camera.zoomOut(); } void Viewer :: mScreenshot( void ) { static int index = 0; // get window width and height GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); int w = view[2]; int h = view[3]; // get pixels Image image( w, h ); glReadPixels( 0, 0, w, h, GL_BGR, GL_FLOAT, &image(0,0) ); stringstream filename; filename << "frames/viewer" << setw(8) << setfill( '0' ) << index << ".tga"; image.write( filename.str().c_str() ); index++; } void Viewer :: display( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); shader.enable(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); double aspect = (double) viewport[2] / (double) viewport[3]; const double fovy = 50.; const double clipNear = .01; const double clipFar = 1000.; gluPerspective( fovy, aspect, clipNear, clipFar ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); Quaternion eye = Vector( 0., 0., -2.5*camera.zoom ); Quaternion center = Vector( 0., 0., 0. ); Quaternion up = Vector( 0., 1., 0. ); gluLookAt( eye[1], eye[2], eye[3], center[1], center[2], center[3], up[1], up[2], up[3] ); Quaternion r = camera.currentRotation(); eye = r.conj() * eye * r; GLint uniformEye = glGetUniformLocation( shader, "eye" ); glUniform3f( uniformEye, eye[1], eye[2], eye[3] ); Quaternion light = Vector( -1., 1., -2. ); light = r.conj() * light * r; GLint uniformLight = glGetUniformLocation( shader, "light" ); glUniform3f( uniformLight, light[1], light[2], light[3] ); camera.setView(); callDisplayList(); shader.disable(); glutSwapBuffers(); } void Viewer :: updateDisplayList( void ) { if( surfaceDL ) { glDeleteLists( surfaceDL, 1 ); surfaceDL = 0; } surfaceDL = glGenLists( 1 ); glNewList( surfaceDL, GL_COMPILE ); setMeshMaterial(); drawScene(); glEndList(); } void Viewer :: setGL( void ) { glClearColor( .5, .5, .5, 1. ); setLighting(); } void Viewer :: setLighting( void ) { GLfloat position[4] = { 20., 30., 40., 0. }; glLightfv( GL_LIGHT0, GL_POSITION, position ); glEnable( GL_LIGHT0 ); glEnable( GL_NORMALIZE ); } void Viewer :: setMeshMaterial( void ) { GLfloat diffuse[4] = { .8, .5, .3, 1. }; GLfloat specular[4] = { .3, .3, .3, 1. }; GLfloat ambient[4] = { .2, .2, .5, 1. }; glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular ); glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient ); glMaterialf ( GL_FRONT_AND_BACK, GL_SHININESS, 16. ); } void Viewer :: callDisplayList( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_DEPTH_TEST ); glEnable( GL_LIGHTING ); glCallList( surfaceDL ); glPopAttrib(); } void Viewer :: drawScene( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_COLOR_MATERIAL ); glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1., 1. ); glColor3d( 1., .5, .25 ); drawPolygons(); glDisable( GL_POLYGON_OFFSET_FILL ); glDisable( GL_COLOR_MATERIAL ); if( renderWireframe ) drawWireframe(); drawIsolatedVertices(); drawSelectedVertices(); glPopAttrib(); } void Viewer :: drawPolygons( void ) { for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { if( f->isBoundary() ) continue; glBegin( GL_POLYGON ); if( renderWireframe ) { Vector N = f->normal(); glNormal3dv( &N[0] ); } HalfEdgeCIter he = f->he; do { if( not renderWireframe ) { Vector N = he->vertex->normal(); glNormal3dv( &N[0] ); } glVertex3dv( &he->vertex->position[0] ); he = he->next; } while( he != f->he ); glEnd(); } } void Viewer :: drawWireframe( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glDisable( GL_LIGHTING ); glColor4f( 0., 0., 0., 0.5 ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBegin( GL_LINES ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { glVertex3dv( &e->he->vertex->position[0] ); glVertex3dv( &e->he->flip->vertex->position[0] ); } glEnd(); glPopAttrib(); } void Viewer :: drawIsolatedVertices( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glPointSize( 5 ); glHint( GL_POINT_SMOOTH_HINT, GL_NICEST ); glEnable( GL_POINT_SMOOTH ); glColor3f( 1., 0., 0. ); glBegin( GL_POINTS ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { glVertex3dv( &v->position[0] ); } } glEnd(); glPopAttrib(); } void Viewer :: drawVertices( void ) { for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { glLoadName(v->index); glBegin(GL_POINTS); glVertex3dv( &v->position[0] ); glEnd(); } } void Viewer :: drawSelectedVertices( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_COLOR_MATERIAL ); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glColor3f( 0., 0.5, 0.5 ); double h = 0.75*mesh.meanEdgeLength(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->tag ) { glPushMatrix(); glTranslated(v->position.x, v->position.y, v->position.z); glutSolidSphere(h, 10, 10); glPopMatrix(); } } glEnd(); glPopAttrib(); } void Viewer :: pickVertex(int x, int y) { int width = glutGet(GLUT_WINDOW_WIDTH ); int height = glutGet(GLUT_WINDOW_HEIGHT); if( x < 0 || x >= width || y < 0 || y >= height ) return; int bufSize = mesh.vertices.size(); GLuint* buf = new GLuint[bufSize]; glSelectBuffer(bufSize, buf); GLint viewport[4]; GLdouble projection[16]; glGetIntegerv( GL_VIEWPORT, viewport ); glGetDoublev(GL_PROJECTION_MATRIX, projection); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix(x, viewport[3]-y, 10, 10, viewport); glMultMatrixd(projection); glMatrixMode(GL_MODELVIEW); glPushMatrix(); drawVertices(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); long hits = glRenderMode(GL_RENDER); int index = -1; double min_z = 1.0e100; for( long i = 0; i < hits; ++i ) { double distance = buf[4*i + 1]; if( distance < min_z ) { index = buf[4*i + 3]; min_z = distance; } } delete[] buf; if (index >= 0) { mesh.vertices[index].toggleTag(); updateDisplayList(); } } } ================================================ FILE: BaseCode/src/main.cpp ================================================ #include using namespace std; #include "Viewer.h" #include "DenseMatrix.h" using namespace DDG; int main( int argc, char** argv ) { if( argc != 2 ) { cerr << "usage: " << argv[0] << " in.obj" << endl; return 1; } Viewer viewer; viewer.mesh.read( argv[1] ); viewer.init(); return 0; } ================================================ FILE: Connection/Makefile ================================================ ########################################################################################## # Specify library locations here (add or remove "#" marks to comment/uncomment lines for your platform) # Mac OS X DDG_INCLUDE_PATH = DDG_LIBRARY_PATH = DDG_BLAS_LIBS = -framework Accelerate DDG_SUITESPARSE_LIBS = -lspqr -lumfpack -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -ltbb -lm -lsuitesparseconfig DDG_OPENGL_LIBS = -framework OpenGL -framework GLUT # # Linux # DDG_INCLUDE_PATH = # DDG_LIBRARY_PATH = # DDG_BLAS_LIBS = -llapack -lblas -lgfortran # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut -lGL -lGLU -lX11 # # Windows / Cygwin # DDG_INCLUDE_PATH = -I/usr/include/opengl -I/usr/include/suitesparse # DDG_LIBRARY_PATH = -L/usr/lib/w32api -L/usr/lib/suitesparse # DDG_BLAS_LIBS = -llapack -lblas # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut32 -lglu32 -lopengl32 ######################################################################################## TARGET = connection CC = g++ LD = g++ CFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_INCLUDE_PATH) -I./include -I./src LFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_LIBRARY_PATH) LIBS = $(DDG_OPENGL_LIBS) $(DDG_SUITESPARSE_LIBS) $(DDG_BLAS_LIBS) ######################################################################################## ## !! Do not edit below this line HEADERS := $(wildcard include/*.h) SOURCES := $(wildcard src/*.cpp) OBJECTS := $(addprefix obj/,$(notdir $(SOURCES:.cpp=.o))) all: $(TARGET) $(TARGET): $(OBJECTS) $(LD) $(OBJECTS) -o $(TARGET) $(CFLAGS) $(LFLAGS) $(LIBS) obj/%.o: src/%.cpp ${HEADERS} $(CC) -c $< -o $@ $(CFLAGS) clean: rm -f $(OBJECTS) rm -f $(TARGET) rm -f $(TARGET).exe ================================================ FILE: Connection/include/Application.h ================================================ /* * Trivial Connections on Discrete Surfaces * Keenan Crane, Mathieu Desbun and Peter Schroeder * SGP 2010 / Computer Graphics Forum * * A Simplified Algorithm for Simply-Connected Surfaces * Fernando de Goes and Keenan Crane * * TODO: add soft and hard directional constraints */ #ifndef DDG_APPLICATION_H #define DDG_APPLICATION_H #include "Mesh.h" #include "Real.h" #include "Quaternion.h" #include "DenseMatrix.h" #include "SparseMatrix.h" #include "DiscreteExteriorCalculus.h" #include "Utility.h" namespace DDG { class Application { public: bool solveForConneciton(Mesh& mesh) { bool ok = checkGaussBonnet(mesh); if( not ok ) { std::cout << "Gauss-Bonnet thm does not hold" << std::endl; return false; } int t0 = clock(); solveForTrivialHolonomy(mesh); int t1 = clock(); cout << "[trivial] time: " << seconds( t0, t1 ) << "s" << "\n"; t0 = clock(); solveForNonTrivialHolonomy(mesh); t1 = clock(); cout << "[nontrivial] time: " << seconds( t0, t1 ) << "s" << "\n"; return true; } protected: bool checkGaussBonnet(const Mesh& mesh) const { // vertex singularity int k = 0; for(VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++) { k += v->singularity; } // generator singularity // TODO: include singularity for all generators k += mesh.firstGeneratorIndex; return ( mesh.getEulerCharacteristicNumber() == k ); } void solveForTrivialHolonomy(Mesh& mesh) { // Neumann boundary condition => prescribing geodesic curvature // For now, keeping original geodesic curvature DenseMatrix b( mesh.vertices.size() ); for(VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++) { double value = 0.0; if( not v->onBoundary() ) { value -= ( 2. * M_PI - v->theta() ); value += 2. * M_PI * v->singularity; } b( v->index ) = value; } DenseMatrix u( mesh.vertices.size() ); if( b.norm() > 1.0e-8 ) backsolvePositiveDefinite( mesh.L, u, b ); for(VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++) { v->potential = u( v->index ); } } void solveForNonTrivialHolonomy(Mesh& mesh) { unsigned nb = mesh.numberHarmonicBases(); if( nb == 0 ) return; mesh.harmonicCoefs = std::vector(nb, 0.0); DenseMatrix b(nb); SparseMatrix H(nb,nb); int row = 0; bool skipBoundaryLoop = true; for(unsigned i = 0; i < mesh.generators.size(); ++i) { const Mesh::Generator& cycle = mesh.generators[i]; if( skipBoundaryLoop and mesh.isBoundaryGenerator(cycle) ) { skipBoundaryLoop = false; continue; } for(unsigned j = 0; j < cycle.size(); ++j) { HalfEdgeIter he = cycle[j]; for(unsigned col = 0; col < nb; ++col) { H(row,col) += he->harmonicBases[col]; } } double value = - mesh.generatorHolonomy( cycle ); if( row == 0 ) { value += 2.0 * M_PI * mesh.firstGeneratorIndex ; } b(row) = value; row++; } DenseMatrix x(nb); if( b.norm() > 1.0e-8 ) solve(H, x, b); for(unsigned i = 0; i < nb; ++i) mesh.harmonicCoefs[i] = x(i); } }; } #endif ================================================ FILE: Connection/include/Camera.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Camera.h // ----------------------------------------------------------------------------- // // Camera is used by Viewer to keep track of the view state; it also // handles mouse input related to camera manipulation. // #ifndef DDG_CAMERA_H #define DDG_CAMERA_H #include "Quaternion.h" #ifdef __CYGWIN__ #define GLUT_DISABLE_ATEXIT_HACK #include #include #include #include #else #include #endif namespace DDG { class Camera { public: Camera( void ); // constructor Quaternion clickToSphere( int x, int y ); // projects a mous click onto the unit sphere void setView( void ) const; // applies the camera transformation to the OpenGL modelview stack void mouse( int button, int state, int x, int y ); // handles mouse clicks void motion( int x, int y ); // handles mouse drags void idle( void ); // handles camera momentum void zoomIn( void ); // moves viewer toward object void zoomOut( void ); // moves viewer away from object Quaternion currentRotation( void ) const; // returns the rotation corresponding to the current mouse state Quaternion pClick; // mouse coordinates of current click Quaternion pDrag; // mouse coordinates of current drag Quaternion pLast; // mouse coordinates of previous drag Quaternion rLast; // previous camera rotation Quaternion momentum; // camera momentum int tLast; // time of previous drag double zoom, vZoom; // zoom and zoom velocity }; } #endif ================================================ FILE: Connection/include/Complex.h ================================================ #ifndef DDG_COMPLEX_H #define DDG_COMPLEX_H #include namespace DDG { class Complex { public: Complex( double a=0., double b=0. ); // constructs number a+bi void operator+=( const Complex& z ); // add z void operator-=( const Complex& z ); // subtract z void operator*=( const Complex& z ); // Complex multiply by z void operator*=( double r ); // scalar multiply by r void operator/=( double r ); // scalar divide by r void operator/=( const Complex& z ); // complex divide by r Complex operator-( void ) const; // returns the additive inverse Complex conj( void ) const; // returns Complex conjugate Complex inv( void ) const; // returns inverse double arg( void ) const; // returns argument double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Complex unit( void ) const; // returns complex number with unit norm and same modulus Complex exponential( void ) const; // complex exponentiation double re; // real part double im; // imaginary part }; Complex operator+( const Complex& z1, const Complex& z2 ); // binary addition Complex operator-( const Complex& z1, const Complex& z2 ); // binary subtraction Complex operator*( const Complex& z1, const Complex& z2 ); // binary Complex multiplication Complex operator*( const Complex& z, double r ); // right scalar multiplication Complex operator*( double r, const Complex& z ); // left scalar multiplication Complex operator/( const Complex& z, double r ); // scalar division Complex operator/( const Complex& z1, const Complex& z2 ); // complex division double dot( const Complex& z1, const Complex& z2 ); // inner product double cross( const Complex& z1, const Complex& z2 ); // cross product std::ostream& operator<<( std::ostream& os, const Complex& o ); // prints components } #endif ================================================ FILE: Connection/include/DenseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DenseMatrix.h // ----------------------------------------------------------------------------- // // DenseMatrix represents an m by n (real or complex) matrix where every // entry -- including zero-valued entries -- is stored explicitly. This // class is most commonly used to represent dense vectors in sparse linear // systems (i.e., the right hand side and the solution vector). // // A real or complex matrix is allocated via // // DenseMatrix A( m, n ); // DenseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // DenseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a DenseMatrix returns a cholmod_dense* // which can be used by routines in SuiteSparse. For basic operations, however, // you should not need to access this pointer explicitly -- see the solve() // method in SparseMatrix.h. // #ifndef DDG_DENSEMATRIX_H #define DDG_DENSEMATRIX_H #include #include "Types.h" #include namespace DDG { enum NormType { lInfinity, lOne, lTwo }; template class DenseMatrix { public: DenseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix (specifying just m yields a column vector) DenseMatrix( const DenseMatrix& A ); // copy constructor const DenseMatrix& operator=( const DenseMatrix& B ); // copies B ~DenseMatrix( void ); // destructor SparseMatrix sparse( void ); // converts to a sparse matrix int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val = 0. ); // sets all elements to val double norm( NormType type = lInfinity ) const; // returns the maximum magnitude of any entry T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element of the matrix (uses 0-based indexing) T& operator()( int index ); T operator()( int index ) const; // access the specified element of a vector (uses 0-based indexing) DenseMatrix transpose( void ) const; // returns the transpose of this matrix DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c DenseMatrix operator+( const DenseMatrix& B ) const; // returns sum of this matrix with B void operator+=( const DenseMatrix& B ); // adds B to this matrix DenseMatrix operator-( const DenseMatrix& B ) const; // returns difference of this matrix with B void operator-=( const DenseMatrix& B ); // subtracts B from this matrix DenseMatrix operator-( void ) const; // returns additive inverse of this matrix cholmod_dense* to_cholmod( void ); // returns pointer to copy of matrix in CHOLMOD format const DenseMatrix& operator=( cholmod_dense* B ); // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B void normalize( void ); // divides by Frobenius norm T sum( void ) const; // returns the sum of all entries void removeMean( void ); // removes the mean void randomize( void ); // replaces entries with uniformly distributed random real numbers in the interval [-1,1] protected: int m, n; std::vector data; cholmod_dense* cData; }; template DenseMatrix operator*( const DenseMatrix& A, const T& c ); // right scalar multiplication template DenseMatrix operator*( const T& c, const DenseMatrix& A ); // left scalar multiplication template DenseMatrix operator/( const DenseMatrix& A, const T& c ); // scalar division template T dot( const DenseMatrix& x, const DenseMatrix& y ); // returns Euclidean inner product of x and y template std::ostream& operator << (std::ostream& os, const DenseMatrix& o); // prints entries template T inner( const DenseMatrix& x, const DenseMatrix& y ); // standard inner product template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ); // inner product with respect to a diagonal inner // product B represented as a dense vector } #include "DenseMatrix.inl" #endif ================================================ FILE: Connection/include/Direction.h ================================================ #ifndef DDG_DIRECTION_H #define DDG_DIRECTION_H #include "Mesh.h" #include "Quaternion.h" namespace DDG { class DirectionField { public: void generate(Mesh& mesh, double angle, bool debugMode = false) { setFaceTag(mesh, false); FaceIter f = mesh.faces.begin(); init(f, angle); HalfEdgeIter he = f->he; do { propagate( mesh, he->flip, f->vector ); he = he->next; } while( he != f->he ); if( debugMode ) debug(mesh); } protected: void setFaceTag(Mesh& mesh, bool flag) { for( FaceIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++) { f->tag = flag; } } void init(FaceIter f, double angle) { VertexIter v0 = f->he->vertex; VertexIter v1 = f->he->next->vertex; Vector v = (v1->position - v0->position).unit(); f->vector = rotate(v, f->normal(), angle); f->tag = true; } void propagate(const Mesh& mesh, HalfEdgeIter h, const Vector& v) { if( h->onBoundary ) return; if( h->face->tag ) return; FaceIter f = h->face; f->vector = transport(mesh, v, h); f->tag = true; HalfEdgeIter he = f->he; do { propagate( mesh, he->flip, f->vector ); he = he->next; } while( he != f->he ); } Vector transport(const Mesh& mesh, const Vector& v, HalfEdgeIter h) const { if( h->onBoundary or h->flip->onBoundary ) return v; Vector p1 = h->flip->vertex->position; Vector p0 = h->vertex->position; Vector e = (p1 - p0).unit(); Vector nR = h->flip->face->normal(); Vector nL = h->face->normal(); Vector nLxnR = cross( nL, nR ); double dihedral = atan2( nLxnR.norm(), dot( nL, nR ) ); if( dot( e, nLxnR ) < 0.0 ) dihedral = -dihedral; Vector u = rotate( v, nR, -mesh.connectionOneForm(h) ); return rotate( u, e, dihedral ); } Vector rotate(const Vector& v, const Vector& axis, double angle) const { angle *= 0.5; Quaternion q( std::cos(angle), std::sin(angle) * axis ); return ( q.conj() * Quaternion(v) * q ).im(); } // debug // void debug(Mesh& mesh) const { std::cout << "DEBUG-BEGIN" << std::endl; for( VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++) { if( v->onBoundary() ) continue; Vector n = v->he->flip->face->normal(); Vector u0 = v->he->flip->face->vector; Vector u = u0; HalfEdgeIter h = v->he; do { u = transport(mesh, u, h); h = h->next->next->flip; } while( h != v->he ); double offset = atan2( cross(u,u0).norm(), dot(u,u0) ); if( dot( n, cross(u,u0) ) > 0.0 ) offset = -offset; while( offset < 0.0 ) offset += 2.*M_PI; while( offset >= 2.0*M_PI ) offset -= 2.*M_PI; if( std::abs(offset-2.0*M_PI) < 1.0e-6 ) offset = 0.0; double sing = mesh.vertexHolonomy(v); while( sing < 0.0 ) sing += 2.*M_PI; while( sing >= 2.0*M_PI ) sing -= 2.*M_PI; if( std::abs(sing-2.0*M_PI) < 1.0e-6 ) sing = 0.0; if( std::abs(offset-sing) > 1.0e-8 ) { std::cout << "Vertex" << v->index << ": " << "tag = " << v->tag << " ; " << "offset = " << (180./M_PI)*offset << " ; " << "sing = " << (180./M_PI)*sing << std::endl; } } for( unsigned i = 0; i < mesh.generators.size(); ++i ) { const Mesh::Generator& cycle = mesh.generators[i]; if( cycle.empty() ) continue; Vector n = cycle[0]->face->normal(); Vector u0 = cycle[0]->face->vector; Vector u = u0; for( int k = cycle.size()-1; k >= 0; k-- ) { u = transport(mesh, u, cycle[k]); } double offset = atan2( cross(u,u0).norm(), dot(u,u0) ); if( dot( n, cross(u,u0) ) > 0.0 ) offset = -offset; while( offset < 0.0 ) offset += 2.*M_PI; while( offset >= 2.0*M_PI ) offset -= 2.*M_PI; if( std::abs(offset-2.0*M_PI) < 1.0e-6 ) offset = 0.0; double sing = mesh.generatorHolonomy(cycle); while( sing < 0.0 ) sing += 2.*M_PI; while( sing >= 2.0*M_PI ) sing -= 2.*M_PI; if( std::abs(sing-2.0*M_PI) < 1.0e-6 ) sing = 0.0; if( std::abs(offset-sing) > 1.0e-8 ) { std::cout << "Generator" << i << ": " << "offset = " << (180./M_PI)*offset << " ; " << "sing = " << (180./M_PI)*sing << std::endl; } } std::cout << "DEBUG-END" << std::endl; } }; } #endif ================================================ FILE: Connection/include/DiscreteExteriorCalculus.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DiscreteExteriorCalculus.h // ----------------------------------------------------------------------------- // // Static methods for building the fundamental discrete operators (exterior // derivative, Hodge star) for 0-, 1-, and 2-forms on a surface mesh. Methods // are templated on entry type, i.e., one can build either real- or complex- // matrices using the types DDG::Real and DDG::Complex, respectively. For // instance, to build the usual Laplacian on functions, one could write // // Mesh mesh; // SparseMatrix d0, star0, star1, Delta; // // ExteriorDerivative0Form::build( mesh, d0 ); // HodgeStar0Form::build( mesh, star0 ); // HodgeStar1Form::build( mesh, star1 ); // Delta = star0.inverse() * d0.transpose() * star1 * d0; // #ifndef DDG_DISCRETEEXTERIORCALCULUS_H #define DDG_DISCRETEEXTERIORCALCULUS_H #include "Mesh.h" #include "SparseMatrix.h" namespace DDG { template< class T > struct HodgeStar0Form { static void build( const Mesh& mesh, SparseMatrix& star0 ); }; template< class T > struct HodgeStar1Form { static void build( const Mesh& mesh, SparseMatrix& star1 ); }; template< class T > struct HodgeStar2Form { static void build( const Mesh& mesh, SparseMatrix& star2 ); }; template< class T > struct ExteriorDerivative0Form { static void build( const Mesh& mesh, SparseMatrix& d0 ); }; template< class T > struct ExteriorDerivative1Form { static void build( const Mesh& mesh, SparseMatrix& d1 ); }; } #include "DiscreteExteriorCalculus.inl" #endif ================================================ FILE: Connection/include/Edge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Edge.h // ----------------------------------------------------------------------------- // // Edge stores attributes associated with a mesh edge. The iterator he points // to one of its two associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_EDGE_H #define DDG_EDGE_H #include "Types.h" namespace DDG { class Edge { public: HalfEdgeIter he; // points to one of the two halfedges associated with this edge int index; // unique integer ID in the range 0, ..., nEdges-1 Edge() : index(0) { } }; } #endif ================================================ FILE: Connection/include/Face.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Face.h // ----------------------------------------------------------------------------- // // Face stores attributes associated with a mesh edge. The iterator he points // to one of its associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_FACE_H #define DDG_FACE_H #include "Vector.h" #include "Types.h" namespace DDG { class Face { public: HalfEdgeIter he; // points to one of the halfedges associated with this face int index; // unique integer ID in the range 0, ..., nFaces-1 Vector vector; // unit vector tangent to triangle bool tag; // auxiliary tag FaceIter parent; // parent in tree-cotree decomposition Face() : index(0), vector(), tag(false) { } bool isBoundary( void ) const; // returns true if this face corresponds to a // boundary loop; false otherwise double area( void ) const; // returns the triangle area Vector normal( void ) const; // returns the unit normal associated with this face; normal // orientation is determined by the circulation order of halfedges Vector circumcenter( void ) const; // returns triangle circumcenter Vector barycenter( void ) const; // returns triangle barycenter }; } #endif ================================================ FILE: Connection/include/HalfEdge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- HalfEdge.h // ----------------------------------------------------------------------------- // // HalfEdge is used to define mesh connectivity. (See the documentation for a // more in-depth discussion of the halfedge data structure.) // #ifndef DDG_HALFEDGE_H #define DDG_HALFEDGE_H #include "Vector.h" #include "Types.h" namespace DDG { class HalfEdge { public: HalfEdgeIter next; // points to the next halfedge around the current face HalfEdgeIter flip; // points to the other halfedge associated with this edge VertexIter vertex; // points to the vertex at the "tail" of this halfedge EdgeIter edge; // points to the edge associated with this halfedge FaceIter face; // points to the face containing this halfedge bool onBoundary; // true if this halfedge is contained in a boundary // loop; false otherwise Vector texcoord; // texture coordinates associated with the triangle corner at the // "tail" of this halfedge std::vector harmonicBases; // value of harmonic bases at halfedge double cotan( void ) const; // returns the cotangent of the angle opposing this edge Vector rotatedEdge( void ) const; // returns oriented edge vector rotated by PI/2 around face normal // if onBoundary, then return nil double angle( void ) const; // returns corner angle at vertex opposite to the halfedge }; } #endif ================================================ FILE: Connection/include/HarmonicBases.h ================================================ #ifndef DDG_HARMONIC_BASES_H #define DDG_HARMONIC_BASES_H #include "Mesh.h" #include "Real.h" #include "DenseMatrix.h" #include "SparseMatrix.h" #include "DiscreteExteriorCalculus.h" namespace DDG { class HarmonicBases { public: void compute(Mesh& mesh) { cleanHalfEdges( mesh ); if( mesh.generators.empty() ) return; SparseMatrix star1, d0, div; HodgeStar1Form::build( mesh, star1 ); ExteriorDerivative0Form::build( mesh, d0 ); div = d0.transpose() * star1; bool skipBoundaryLoop = true; for(unsigned i = 0; i < mesh.generators.size(); ++i) { const Mesh::Generator& cycle = mesh.generators[i]; if( skipBoundaryLoop and mesh.isBoundaryGenerator(cycle) ) { skipBoundaryLoop = false; continue; } DenseMatrix w; buildClosedPrimalOneForm(mesh, cycle, w); DenseMatrix u; DenseMatrix divw = div * w; backsolvePositiveDefinite( mesh.L, u, divw ); DenseMatrix h = star1*( w - (d0*u) ); storeHarmonicForm(h, mesh); } } protected: void buildClosedPrimalOneForm(const Mesh& mesh, const Mesh::Generator& cycle, DenseMatrix& oneform) const { oneform = DenseMatrix( mesh.edges.size() ); for(unsigned i = 0; i < cycle.size(); ++i) { double value = 1.0; HalfEdgeIter he = cycle[i]; if( he->edge->he != he ) value = -value; oneform( he->edge->index ) = value; } } void storeHarmonicForm(const DenseMatrix& oneform, Mesh& mesh) { for(EdgeIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++) { double value = oneform(e->index); e->he->harmonicBases.push_back( value ); e->he->flip->harmonicBases.push_back( -value ); } } void cleanHalfEdges(Mesh& mesh) { for(EdgeIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++) { e->he->harmonicBases.clear(); e->he->flip->harmonicBases.clear(); } } }; } #endif ================================================ FILE: Connection/include/Image.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Image.h // ----------------------------------------------------------------------------- // // Image represents a color bitmap image. A simple example might look like // // Image im; // im.read( "input.tga" ); // // modify image data via im(x,y) = ...; // im.write( "output.tga" ); // #ifndef DDG_IMAGE_H #define DDG_IMAGE_H #include #include namespace DDG { class Image { public: Image( int width = 0, int height = 0 ); // constructs image with specified width and height float& operator()( int x, int y ); const float& operator()( int x, int y ) const; // accesses pixel (x,y) float sample( float x, float y ) const; // samples image at (x,y) using bilinear filtering int width( void ) const; int height( void ) const; // returns image dimensions void read( const char* filename ); // loads an image file in Truevision TGA format // (must be RGB image with 24 or 32 bits per pixel) void write( const char* filename ) const; // writes an image file in Truevision TGA format // (RGB image with 24 bits per pixel) protected: void clamp( int& x, int& y ) const; // clamps coordinates to range [0,w-1] x [0,h-1] int w, h; // width and height std::vector pixels; // interleaved RGBA pixel data in range [0-1] }; } #endif ================================================ FILE: Connection/include/LinearContext.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearContext.h // ----------------------------------------------------------------------------- // // LinearContext is the global solver context needed to interface with the // SuiteSparse library. It is essentially a wrapper around cholmod_common. A // single static instance of LinearContext is declared in LinearContext.cpp and // is shared by all instances of DenseMatrix, SparseMatrix, and LinearSystem. // In other words, you shouldn't have to instantiate LinearContext yourself // unless you're doing something really fancy! // #ifndef DDG_LINEARSOLVERCONTEXT #define DDG_LINEARSOLVERCONTEXT #include namespace DDG { class LinearContext { public: LinearContext( void ); // constructor ~LinearContext( void ); // destructor operator cholmod_common*( void ); // allows LinearContext to be treated as a cholmod_common* protected: cholmod_common context; }; } #endif ================================================ FILE: Connection/include/LinearEquation.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearEquation.h // ----------------------------------------------------------------------------- // // LinearEquation represents an equation with an arbitrary linear polynomial on // both the left- and right-hand side. It is primarily used while building a // LinearSystem. For convenience, operator== is overloaded so that the user // can construct a LinearEquation by writing something that looks much like the // usual mathematical syntax for a linear equation. For example, // // LinearEquation eqn = ( x + 2*y == 3*z ); // // builds the linear equation x + 2y = 3z. // #ifndef DDG_LINEAREQUATION_H #define DDG_LINEAREQUATION_H #include "LinearPolynomial.h" namespace DDG { class LinearEquation { public: LinearPolynomial lhs; // left-hand side LinearPolynomial rhs; // right-hand side }; LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ); // constructs a linear equation with the specified left- and right-hand side } #endif ================================================ FILE: Connection/include/LinearPolynomial.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearPolynomial.h // ----------------------------------------------------------------------------- // // LinearPolynomial represents an affine function of the form // // f(x1,x2,...,xn) = c1 x1 + c2 x2 + ... + cn xn + d // // where the xi are real-valued variables with real coefficients ci, and d is // a real constant. The variables and their coefficients are represented using // instances of the Variable class. LinearPolynomial implements all the usual // algebraic operations on affine functions, as well as type conversions from // more elementary types (scalars, single variables, etc.). // // Importantly, variables used in a LinearPolynomial should *not* be deallocated // while the polynomial is still in use -- LinearPolynomial stores only a // reference to these variables so that the solution to a linear system can be // automatically copied back into the variables. // #ifndef DDG_LINEARPOLYNOMIAL_H #define DDG_LINEARPOLYNOMIAL_H #include #include #include "Variable.h" namespace DDG { class LinearPolynomial { public: LinearPolynomial( void ); // constructs the zero function LinearPolynomial( double c ); // constructs the constant function with value c LinearPolynomial( Variable& v ); // constructs a function with a single variable v const LinearPolynomial& operator=( double c ); // assigns the constant function with value c const LinearPolynomial& operator=( Variable& v ); // assigns a function with a single variable v void operator+=( double c ); void operator-=( double c ); void operator*=( double c ); void operator/=( double c ); // adds, subtract, multiplies, or divides by a constant void operator+=( Variable& v ); void operator-=( Variable& v ); // increments or decrements by a single variable v void operator+=( const LinearPolynomial& p ); void operator-=( const LinearPolynomial& p ); // increments or decrements by an affine function LinearPolynomial operator-( void ) const; // returns the additive inverse (i.e., negation) double evaluate( void ) const; // evaluates the function using the current values of its variables std::map linearTerms; // list of linear terms double constantTerm; // constant term }; LinearPolynomial operator+( double c, Variable& v ); // sum LinearPolynomial operator+( Variable& v, double c ); // sum LinearPolynomial operator-( double c, Variable& v ); // difference LinearPolynomial operator-( Variable& v, double c ); // difference LinearPolynomial operator*( double c, Variable& v ); // product LinearPolynomial operator*( Variable& v, double c ); // product LinearPolynomial operator/( Variable& v, double c ); // quotient // algebraic operations between single variables and constants LinearPolynomial operator+( Variable& v1, Variable& v2 ); // sum LinearPolynomial operator-( Variable& v1, Variable& v2 ); // difference // algebraic operations between pairs of variables LinearPolynomial operator+( const LinearPolynomial& p, double c ); // sum LinearPolynomial operator+( double c, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, double c ); // difference LinearPolynomial operator-( double c, const LinearPolynomial& p ); // difference LinearPolynomial operator*( const LinearPolynomial& p, double c ); // product LinearPolynomial operator*( double c, const LinearPolynomial& p ); // product LinearPolynomial operator/( const LinearPolynomial& p, double c ); // quotient // algebraic operations between polynomials and constants LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ); // sum LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ); // difference LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ); // difference // algebraic operations between polynomials and single variables LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ); // sum LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ); // difference // algebraic operations between pairs of polynomials std::ostream& operator<<( std::ostream& os, const LinearPolynomial& p ); // prints the symbolic representation of a polynomial (all variables must be named) } #endif ================================================ FILE: Connection/include/LinearSystem.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearSystem.h // ----------------------------------------------------------------------------- // // LinearSystem represents a system of linear equations expressed in terms of // instances of the Variable class. The main idea is to make it easy to // construct and solve linear systems without explicitly think about variable // indices, matrix layout, etc. (This kind of abstraction is particularly // useful for debugging and rapid prototyping.) See the documentation for // examples of building linear and solving systems. // // Importantly, any variable used by a LinearSystem should not be deallocated // while the system is still in use, because the method LinearSystem::solve() // automatically copies the solution back into the variables used to define the // equations. (In the future variables may become reference-counted in order // to avoid this issue.) // // Note that LinearSystem::solve() uses a general-purpose linear solver (namely // the sparse QR factorization found in SuiteSparse) that is quite fast but may // not always be your best option. To improve performance you may want to // build the system explicitly using an instance of SparseMatrix and call a // more specialized solver. (In the future there may be options for specifying // that a LinearSystem is, e.g., symmetric and positive-definite.) // #ifndef DDG_LINEARSYSTEM_H #define DDG_LINEARSYSTEM_H #include #include "LinearEquation.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "Real.h" namespace DDG { class LinearSystem { public: void clear( void ); // removes all equations from the system void push_back( const LinearEquation& e ); // appends the equation e to the sytem void solve( void ); // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution std::vector equations; // the collection of equations defining the system protected: void convertEquations( void ); void indexVariables( void ); void buildSparseMatrix( void ); void buildRightHandSide( void ); void computeSolution( void ); int nEquations; int nVariables; std::vector currentEquations; std::map index; SparseMatrix A; DenseMatrix x; DenseMatrix b; }; } #endif ================================================ FILE: Connection/include/Mesh.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Mesh.h // ----------------------------------------------------------------------------- // // Mesh represents a polygonal surface mesh using the halfedge data structure. // It is essentially a large collection of disjoint vertices, edges, and faces // that are ``glued together'' by halfedges which encode connectivity (see // the documentation for an illustration). By construction, the halfedge data // structure cannot represent nonorientable surfaces or meshes with nonmanifold // edges. // // Mesh elements are referenced using iterators -- common usage of these // iterators is to either traverse an entire vector of mesh elements: // // // visit all vertices // for( VertexIter i = vertices.begin(); i != vertices.end(); i++ ) // { // //... // } // // or to perform a local traversal over the neighborhood of some mesh element: // // // visit both halfedges of edge e // HalfEdgeIter he = e->he; // do // { // // ... // // he = he->flip; // } // while( he != e->he ); // // (See Types.h for an explicit definition of iterator types.) // // Meshes with boundary are handled by creating an additional face for each // boundary loop (the method Face::isBoundary() determines whether a given // face is a boundary loop). Isolated vertices (i.e., vertiecs not contained // in any edge or face) reference a dummy halfedge and can be checked via // the method Vertex::isIsolated(). // #ifndef DDG_MESH_H #define DDG_MESH_H #include #include #include "HalfEdge.h" #include "Vertex.h" #include "Edge.h" #include "Face.h" #include "SparseMatrix.h" namespace DDG { class Mesh { public: typedef std::vector Generator; Mesh( void ); // constructs an empty mesh Mesh( const Mesh& mesh ); // constructs a copy of mesh const Mesh& operator=( const Mesh& mesh ); // copies mesh int read( const std::string& filename ); // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error int write( const std::string& filename ) const; // writes a mesh to a Wavefront OBJ file; return value is nonzero // only if there was an error bool reload( void ); // reloads a mesh from disk using the most recent input filename void normalize( void ); // centers around the origin and rescales to have unit radius double area( void ) const; // returns total mesh area double meanEdgeLength( void ) const; // returns mean edge lenght int getEulerCharacteristicNumber( void ) const; // returns Euler Characteristic Number bool isBoundaryGenerator(const Generator& cycle) const; // returns true if generator is a boundary loop unsigned numberHarmonicBases() const; // returns 2g + (m-1) where g = genus and m = number of boundary loops double connectionOneForm(HalfEdgeIter h) const; // returns rotation angle by crossing h double parallelTransport(HalfEdgeIter h) const; // returns rotation around h->vertex void faceFrame(HalfEdgeIter h, Vector& a, Vector& b) const; // returns unit edge vector parallel to h and // its rotation by \pi/2 around the face's normal double vertexHolonomy(VertexIter vertex) const; // returns defect angle by rotating around vertex double generatorHolonomy(const Generator& cycle) const; // returns defect angle by rotating around cycle void init(); // pre-compute data std::vector halfedges; std::vector vertices; std::vector edges; std::vector faces; std::vector boundaries; // storage for mesh elements SparseFactor L; // pre-factorization of Laplacian std::vector generators; // non-contractible loops std::vector harmonicCoefs; // coefficients for linear combination of harmonic bases double firstGeneratorIndex; protected: std::string inputFilename; void indexElements( void ); // assigns a unique, 0-based index to each mesh element }; } #endif ================================================ FILE: Connection/include/MeshIO.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- MeshIO.h // ----------------------------------------------------------------------------- // // MeshIO handles input/output operations for Mesh objects. Currently the only // supported mesh format is Wavefront OBJ -- for a format specification see // // http://en.wikipedia.org/wiki/Wavefront_.obj_file // // Note that vertex normals and material properties are currently ignored. // #ifndef DDG_MESHIO_H #define DDG_MESHIO_H #include #include #include #include namespace DDG { class Mesh; class Index; class MeshData; class MeshIO { public: static int read( std::istream& in, Mesh& mesh ); // reads a mesh from a valid, open input stream in static void write( std::ostream& out, const Mesh& mesh ); // writes a mesh to a valid, open output stream out protected: static int readMeshData( std::istream& in, MeshData& data ); static void readPosition( std::stringstream& ss, MeshData& data ); static void readTexCoord( std::stringstream& ss, MeshData& data ); static void readNormal ( std::stringstream& ss, MeshData& data ); static void readFace ( std::stringstream& ss, MeshData& data ); static Index parseFaceIndex( const std::string& token ); static void preallocateMeshElements( const MeshData& data, Mesh& mesh ); static int buildMesh( const MeshData& data, Mesh& mesh ); static void checkIsolatedVertices( const Mesh& Mesh ); static void checkNonManifoldVertices( const Mesh& Mesh ); }; } #endif ================================================ FILE: Connection/include/Quaternion.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Quaternion.h // ----------------------------------------------------------------------------- // // Quaternion represents an element of the quaternions, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // Hamilton product is expressed using the * operator: // // Quaternion p, q, r; // r = q * p; // // and conjugation is expressed using the method Quaternion::conj(): // // Quaternion q; // double normQSquared = -q.conj()*q; // // Individual components can be accessed in several ways: the real and imaginary // parts can be accessed using the methods Quaternion::re() and Quaternion::im(): // // Quaternion q; // double a = q.re(); // Vector b = q.im(); // // or by index: // // Quaternion q; // double a = q[0]; // double bi = q[1]; // double bj = q[2]; // double bk = q[3]; // #ifndef DDG_QUATERNION_H #define DDG_QUATERNION_H #include "Vector.h" #include "Complex.h" #include namespace DDG { class Quaternion { public: Quaternion( void ); // initializes all components to zero Quaternion( const Quaternion& q ); // initializes from existing quaternion Quaternion( double s, double vi, double vj, double vk ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s, const Vector& v ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s ); // initializes purely real quaternion with specified real (s) component (imaginary part is zero) Quaternion( const Vector& v ); // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) Quaternion( const Complex& z ); // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k const Quaternion& operator=( double s ); // assigns a purely real quaternion with real value s const Quaternion& operator=( const Vector& v ); // assigns a purely real quaternion with imaginary value v double& operator[]( int index ); // returns reference to the specified component (0-based indexing: r, i, j, k) const double& operator[]( int index ) const; // returns const reference to the specified component (0-based indexing: r, i, j, k) void toMatrix( double Q[4][4] ) const; // builds 4x4 matrix Q representing (left) quaternion multiplication double& re( void ); // returns reference to double part const double& re( void ) const; // returns const reference to double part Vector& im( void ); // returns reference to imaginary part const Vector& im( void ) const; // returns const reference to imaginary part Quaternion operator+( const Quaternion& q ) const; // addition Quaternion operator-( const Quaternion& q ) const; // subtraction Quaternion operator-( void ) const; // negation Quaternion operator*( double c ) const; // right scalar multiplication Quaternion operator/( double c ) const; // scalar division void operator+=( const Quaternion& q ); // addition / assignment void operator+=( double c ); // addition / assignment of pure real void operator-=( const Quaternion& q ); // subtraction / assignment void operator-=( double c ); // subtraction / assignment of pure real void operator*=( double c ); // scalar multiplication / assignment void operator/=( double c ); // scalar division / assignment Quaternion operator*( const Quaternion& q ) const; // Hamilton product void operator*=( const Quaternion& q ); // Hamilton product / assignment Quaternion conj( void ) const; // conjugation Quaternion inv( void ) const; // inverse double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Quaternion unit( void ) const; // returns unit quaternion void normalize( void ); // divides by Euclidean length protected: double s; // scalar (double) part Vector v; // vector (imaginary) part }; Quaternion operator*( double c, const Quaternion& q ); // left scalar multiplication std::ostream& operator<<( std::ostream& os, const Quaternion& q ); // prints components } #endif ================================================ FILE: Connection/include/Real.h ================================================ #ifndef DDG_REAL_H #define DDG_REAL_H namespace DDG { class Real { public: Real( double x = 0. ); // constructs real number with value x operator double( void ) const; // type cast to double void operator+=( double x ); // increment void operator-=( double x ); // decrement void operator*=( double x ); // multiply void operator/=( double x ); // divide Real conj( void ) const; // simply returns the value (for compatibility w/ complex numbers) Real inv( void ) const; // returns inverse double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Real unit( void ) const; // returns number with unit norm and same sign protected: double value; // value }; } #endif ================================================ FILE: Connection/include/Shader.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Shader.h // ----------------------------------------------------------------------------- // // Shader encapsulates the functionality of a shader program written in // the OpenGL Shader Language (GLSL). Basic usage is to read a collection // of source files to disk and enable the shader before making draw calls. // For instance, during initialization one might write // // Shader shader; // shader.loadVertex( "vertex.glsl" ); // shader.loadFragment( "fragment.glsl" ); // // and in the main draw routine write // // shader.enable(); // // draw some stuff // shader.disable(); // #ifndef DDG_SHADER_H #define DDG_SHADER_H #include #include namespace DDG { class Shader { public: Shader( void ); // constructor -- shader is initially invalid ~Shader( void ); // destructor void loadVertex( const char* filename ); // read vertex shader from GLSL source file void loadFragment( const char* filename ); // read fragment shader from GLSL source file void loadGeometry( const char* filename ); // read geometry shader from GLSL source file void enable( void ); // uses this shader for rendering void disable( void ) const; // uses the fixed-function pipeline for rendering operator GLuint( void ) const; // returns the ID of this shader program (for calls to OpenGL) protected: void load( GLenum shaderType, const char* filename, GLuint& shader ); bool readSource( const char* filename, std::string& source ); GLuint vertexShader; GLuint fragmentShader; GLuint geometryShader; GLuint program; bool linked; }; } #endif ================================================ FILE: Connection/include/SparseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- SparseMatrix.h // ----------------------------------------------------------------------------- // // SparseMatrix represents an m by n (real or complex) matrix where only // nonzero entries are stored explicitly. This class is most commonly // used to represent the linear term in sparse linear systems (i.e., the matrix // part). // // A real or complex matrix is allocated via // // SparseMatrix A( m, n ); // SparseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // SparseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a SparseMatrix returns a // cholmod_sparse* which can be used by routines in SuiteSparse. For basic // operations, however, you should not need to access this pointer explicitly -- // see the solve() method below. // // Internally SparseMatrix stores nonzero entries in a heap data structure; the // amortized cost of insertion is therefore no worse than the sorting cost of // putting the matrix in compressed-column order. // #ifndef DDG_SPARSE_MATRIX_H #define DDG_SPARSE_MATRIX_H #include #include #include #include "Types.h" namespace DDG { template class SparseMatrix { public: SparseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix SparseMatrix( const SparseMatrix& B ); // copy constructor ~SparseMatrix( void ); // destructor const SparseMatrix& operator=( const SparseMatrix& B ); // copies B const SparseMatrix& operator=( cholmod_sparse* B ); // copies a cholmod_sparse* into a SparseMatrix; // takes responsibility for deallocating B void resize( int m, int n ); // clears and resizes to mxn matrix SparseMatrix transpose( void ) const; // returns the transpose of this matrix cholmod_sparse* to_cholmod( void ); // returns pointer to copy of matrix in compressed-column CHOLMOD format SparseMatrix operator*( const SparseMatrix& B ) const; // returns product of this matrix with sparse B DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with dense B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c void operator+=( const SparseMatrix& B ); // adds B to this matrix void operator-=( const SparseMatrix& B ); // subtracts B from this matrix SparseMatrix operator+( const SparseMatrix& B ) const; // returns sum of this matrix with B SparseMatrix operator-( const SparseMatrix& B ) const; // returns difference of this matrix with B int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val ); // sets all nonzero elements val SparseMatrix inverse( void ) const; // returns inverse -- for diagonal matrices only // (assertion occurs for non-diagonal matrices) static SparseMatrix identity( int N ); // returns the N x N identity matrix DenseMatrix full( void ) const; // converts to a dense matrix T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element (uses 0-based indexing) // TODO for legibility, replace w/ type where entries are named "row, // TODO col" instead of "first, second" (especially since we adopt the // TODO unorthodox convention of storing the column first) typedef std::pair EntryIndex; // convenience type for an entry index; note that we store column THEN // row, which makes it easier to build compressed column format typedef std::map EntryMap; typedef typename EntryMap::iterator iterator; typedef typename EntryMap::const_iterator const_iterator; // convenience types for storing and accessing entries iterator begin( void ); const_iterator begin( void ) const; iterator end( void ); const_iterator end( void ) const; // return iterators to first and last nonzero entries void shift( double c ); // adds c times the identity matrix to this matrix protected: int m, n; EntryMap data; cholmod_sparse* cData; void allocateSparse( void ); void setEntry( const_iterator e, int i, double* pr ); }; template SparseMatrix operator*( const SparseMatrix& A, const T& c ); // right scalar multiplication template SparseMatrix operator*( const T& c, const SparseMatrix& A ); // left scalar multiplication template SparseMatrix operator/( const SparseMatrix& A, const T& c ); // scalar division template std::ostream& operator << (std::ostream& os, const SparseMatrix& o); // prints entries template class SparseFactor { public: SparseFactor( void ); ~SparseFactor( void ); void build( SparseMatrix& A ); // factorizes positive-definite matrix A using CHOLMOD bool valid( void ) const; // returns true if the factor has been built; false otherwise cholmod_factor* to_cholmod( void ); // returns pointer to underlying cholmod_factor data structure protected: cholmod_factor *L; }; template void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse QR factorization template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse LU factorization template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ); // backsolves the prefactored positive definite sparse linear system LL'x = b template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ); // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ); // returns the max residual of the linear problem A x = b relative to the largest entry of the solution template double residual( const SparseMatrix& A, const DenseMatrix& x ); // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda B x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns /<(B-EE^T)x,x> } #include "SparseMatrix.inl" #endif ================================================ FILE: Connection/include/TreeCotree.h ================================================ #ifndef DDG_TREE_COTREE_H #define DDG_TREE_COTREE_H #include #include "Mesh.h" namespace DDG { class TreeCotree { public: void build(Mesh& mesh) { mesh.generators.clear(); buildPrimalSpanningTree(mesh); buildDualSpanningCoTree(mesh); buildCycles(mesh); } protected: void buildCycles(Mesh& mesh) { for( EdgeIter e = mesh.edges.begin(); e != mesh.edges.end(); e++ ) { if( e->he->onBoundary or e->he->flip->onBoundary ) continue; if( inPrimalSpanningTree( e->he ) ) continue; if( inDualSpanningTree( e->he ) ) continue; // path to root from right face Mesh::Generator c1; FaceIter f = e->he->flip->face; while( f != f->parent ) { c1.push_back( sharedHalfEdge( f, f->parent )); f = f->parent; } // path to root from left face Mesh::Generator c2; f = e->he->face; while( f != f->parent ) { c2.push_back( sharedHalfEdge( f, f->parent )); f = f->parent; } // build cycle Mesh::Generator g; g.push_back( e->he ); int m = c1.size()-1; int n = c2.size()-1; while( c1[m] == c2[n] ) { m--; n--; } for( int i = 0; i <= m; i++ ) g.push_back( c1[i] ); for( int i = n; i >= 0; i-- ) g.push_back( c2[i]->flip ); // make sure that boundary loops wind around the boundary // in a consistent direction if( isDualBoundaryLoop( g )) { if( g[0]->next->vertex->onBoundary() ) { // reverse cycle unsigned int n = g.size(); for( unsigned int i = 0; i < n; i++ ) { g[i] = g[i]->flip; } for( unsigned int i = 0; i < n/2; i++ ) { swap( g[i], g[n-1-i] ); } } } mesh.generators.push_back(g); } } void buildPrimalSpanningTree(Mesh& mesh) { // reset parent for( VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) { v->parent = v; } // set root as non-boundary vertex VertexIter root = mesh.vertices.begin(); while( root->onBoundary() ) root++; // spanning tree std::queue Q; Q.push( root ); while( !Q.empty() ) { VertexIter v = Q.front(); Q.pop(); HalfEdgeIter he = v->he; do { VertexIter w = he->flip->vertex; if( w->parent == w and w != root and !w->onBoundary() ) { w->parent = v; Q.push( w ); } he = he->flip->next; } while( he != v->he ); } } void buildDualSpanningCoTree(Mesh& mesh) { // reset parent for( FaceIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) { f->parent = f; } // set root as non-boundary face FaceIter root = mesh.faces.begin(); while( root->isBoundary() ) root++; // spanning tree std::queue Q; Q.push( root ); while( !Q.empty() ) { FaceIter f = Q.front(); Q.pop(); HalfEdgeIter he = f->he; do { FaceIter g = he->flip->face; if( g->parent == g and g != root and !g->isBoundary() and !inPrimalSpanningTree( he ) ) { g->parent = f; Q.push( g ); } he = he->next; } while( he != f->he ); } } bool inPrimalSpanningTree( HalfEdgeIter he ) const { VertexIter v = he->vertex; VertexIter w = he->flip->vertex; return v->parent == w or w->parent == v; } bool inDualSpanningTree( HalfEdgeIter he ) const { FaceIter f = he->face; FaceIter g = he->flip->face; return f->parent == g or g->parent == f; } HalfEdgeIter sharedHalfEdge( FaceIter f, FaceIter g ) const { HalfEdgeIter he = f->he; do { if( he->flip->face == g ) return he; he = he->next; } while( he != f->he ); assert( 0 ); } bool isDualBoundaryLoop( const Mesh::Generator& cycle ) const { if( cycle.size() == 0 ) return false; return ( cycle[0]->vertex->onBoundary() or cycle[0]->flip->vertex->onBoundary() ); } }; } #endif ================================================ FILE: Connection/include/Types.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Types.h // ----------------------------------------------------------------------------- // // This file contains forward declarations of common types and definitions of // convenience types for standard iterators. // #ifndef DDG_TYPES_H #define DDG_TYPES_H #include #include #include namespace DDG { // forward declarations class Camera; class Complex; class Edge; class Face; class HalfEdge; class Image; class LinearContext; class LinearEquation; class LinearPolynomial; class LinearSystem; class Mesh; class MeshIO; class Quaternion; class Real; class Shader; class Variable; class Vector; class Vertex; class Viewer; template class DenseMatrix; template class SparseMatrix; // convenience types for iterators typedef std::map::iterator TermIter; typedef std::map::const_iterator TermCIter; typedef std::vector::iterator PolyIter; typedef std::vector::const_iterator PolyCIter; typedef std::vector::iterator EqnIter; typedef std::vector::const_iterator EqnCIter; typedef std::map::iterator IndexIter; typedef std::map::const_iterator IndexCIter; typedef std::vector::iterator HalfEdgeIter; typedef std::vector::const_iterator HalfEdgeCIter; typedef std::vector::iterator VertexIter; typedef std::vector::const_iterator VertexCIter; typedef std::vector::iterator EdgeIter; typedef std::vector::const_iterator EdgeCIter; typedef std::vector::iterator FaceIter; typedef std::vector::const_iterator FaceCIter; } #endif ================================================ FILE: Connection/include/Utility.h ================================================ #ifndef DDG_UTILITY_H #define DDG_UTILITY_H #include #include "Utility.h" #include "Complex.h" namespace DDG { inline double sqr( double x ) { return x*x; } inline double unitRand( void ) { const double rRandMax = 1. / (double) RAND_MAX; return rRandMax * (double) rand(); } inline double seconds( int t0, int t1 ) { return (double)(t1-t0) / (double) CLOCKS_PER_SEC; } } namespace DDGConstants { static DDG::Complex ii( 0., 1. ); } #endif ================================================ FILE: Connection/include/Variable.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Variable.h // ----------------------------------------------------------------------------- // // Variable represents a variable that can be used to define a (linear or // nonlinear) system of equations. Its main feature is that it can be used // both as an abstract variable (e.g., when used to define an equation) but can // also store a definite numerical value. For instance, suppose we define a // linear polynomial // // Variable x, y; // LinearPolynomial p = x + 2*y; // // Now by assigning different numerical values to x and y, we can evaluate the // polynomial at different points: // // *x = 1; // *y = 2; // cout << p.evaluate() << endl; // // *x = 3; // *y = 4; // cout << p.evaluate() << endl; // // In general the dereference operator (*) accesses the numerical value. // Variables can also be named in order to aid with debugging. For instance, // // Variable x( "x" ); // Variable y( "y" ); // Polynomial p = x + 2*y; // cout << p << endl; // // will print out something like "x+2*y". // // The "fixed" flag in a variable refers to whether it is held constant while // solving a system of equations -- see the documentation for further discussion. // #ifndef DDG_VARIABLE_H #define DDG_VARIABLE_H #include namespace DDG { class Variable { public: Variable( double value = 0., bool fixed = false ); // initialize a variable which has value zero and is not fixed by default Variable( std::string name, double value = 0., bool fixed = false ); // initialize a named variable which has value zero and is not fixed by default double& operator*( void ); // returns a reference to the numerical value const double& operator*( void ) const; // returns a const reference to the numerical value std::string name; // names the variable (for display output) double value; // numerical value bool fixed; // true if a variable is held constant while solving a system of equations }; } #endif ================================================ FILE: Connection/include/Vector.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vector.h // ----------------------------------------------------------------------------- // // Vector represents an element of Euclidean 3-space, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // inner product (i.e., scalar or dot product) is expressed using the global // method dot(): // // Vector u, v; // double cosTheta = dot( u, v ); // // and the cross product is expressed using the global method cross(): // // Vector u, v, w; // w = cross( u, v ); // // Individual components can be accessed in two ways: either directly via the // members x, y, and z: // // Vector v; // cout << v.x << endl; // cout << v.y << endl; // cout << v.z << endl; // // or by index: // // Vector v; // for( int i = 0; i < 3; i++ ) // { // cout << v[i] << endl; // } // #ifndef DDG_VECTOR_H #define DDG_VECTOR_H #include namespace DDG { class Vector { public: Vector(); // initializes all components to zero Vector( double x, double y, double z); // initializes with specified components Vector( const Vector& v ); // initializes from existing vector double& operator[] ( const int& index ); // returns reference to the specified component (0-based indexing: x, y, z) const double& operator[] ( const int& index ) const; // returns const reference to the specified component (0-based indexing: x, y, z) Vector operator+( const Vector& v ) const; // addition Vector operator-( const Vector& v ) const; // subtraction Vector operator-( void ) const; // negation Vector operator*( const double& c ) const; // right scalar multiplication Vector operator/( const double& c ) const; // scalar division void operator+=( const Vector& v ); // addition / assignment void operator-=( const Vector& v ); // subtraction / assignment void operator*=( const double& c ); // scalar multiplication / assignment void operator/=( const double& c ); // scalar division / assignment double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Vector unit( void ) const; // returns unit vector void normalize( void ); // divides by Euclidean length Vector abs( void ) const; // returns vector containing magnitude of each component double x, y, z; // components }; Vector operator* ( const double& c, const Vector& v ); // left scalar multiplication double dot( const Vector& u, const Vector& v ); // dot product (a.k.a. inner or scalar product) Vector cross( const Vector& u, const Vector& v ); // cross product std::ostream& operator << (std::ostream& os, const Vector& o); // prints components } #endif ================================================ FILE: Connection/include/Vertex.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vertex.h // ----------------------------------------------------------------------------- // // Vertex stores attributes associated with a mesh edge. The iterator he // points to its "outgoing" halfedge. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_VERTEX_H #define DDG_VERTEX_H #include "Vector.h" #include "Types.h" namespace DDG { class Vertex { public: HalfEdgeIter he; // points to the "outgoing" halfedge Vector position; // location of vertex in Euclidean 3-space int index; // unique integer ID in the range 0, ..., nVertices-1 bool tag; // true if vertex is selected by the user; false otherwise double potential; // zero-form that controls curvature double singularity; // singularity index VertexIter parent; // parent in tree-cotree decomposition Vertex() : index(0), tag(false), potential(0.0), singularity(0.0) { } double area( void ) const; // returns the barycentric area associated with this vertex Vector normal( void ) const; // returns the vertex normal bool isIsolated( void ) const; // returns true if the vertex is not contained in any face or edge; false otherwise int valence( void ) const; // returns the number of incident faces / edges void toggleTag(); // toggle vertex tag double theta( void ) const; // returns sum_tip_angles bool onBoundary( void ) const; // returns true if vertex is at boundary }; } #endif ================================================ FILE: Connection/include/Viewer.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Viewer.h // ----------------------------------------------------------------------------- // // Viewer provides a graphical user interface (GUI) for inspecting and // interacting with a Mesh object. Viewer methods are static in order // to make them compatible with GLUT callbacks. // #ifndef DDG_VIEWER_H #define DDG_VIEWER_H #include #include "Mesh.h" #include "Camera.h" #include "Shader.h" namespace DDG { class Viewer { public: static void init( void ); // displays the viewer until the program ends static Mesh mesh; // surface mesh visualized by Viewer static double angle; static double increment; // initial angle and increment static unsigned oldest; static unsigned counter; static std::vector indices; static std::vector age; protected: // init static void initGLUT( void ); static void initGLSL( void ); // GLUT callbacks static void display( void ); static void idle( void ); static void keyboard( unsigned char c, int x, int y ); static void special( int i, int x, int y ); static void mouse( int button, int state, int x, int y ); static void motion( int x, int y ); static void menu( int value ); static void view( int value ); // menu functions static void mProcess( void ); static void mResetMesh( void ); static void mWriteMesh( void ); static void mExit( void ); static void mWireframe( void ); static void mZoomIn( void ); static void mZoomOut( void ); static void mScreenshot( void ); static void mIncreaseAngle( void ); static void mDecreaseAngle( void ); static void mVectorField( void ); static void mGenerators( void ); static void mSelected( void ); static void mIncreaseIndexG( void ); static void mDecreaseIndexG( void ); static void mIncreaseIndexV( int i ); static void mDecreaseIndexV( int i ); static void computeOldest( void ); static void normalizeSingularities( void ); static void updateIndex( int i, bool increment ); // unique identifiers for menus enum { menuProcess, menuResetMesh, menuWriteMesh, menuExit, menuWireframe, menuZoomIn, menuZoomOut, menuScreenshot, menuIncreaseAngle, menuDecreaseAngle, menuVectorField, menuGenerators, menuSelected, menuIncreaseIndex0, menuDecreaseIndex0, menuIncreaseIndex1, menuDecreaseIndex1, menuIncreaseIndex2, menuDecreaseIndex2, menuIncreaseIndex3, menuDecreaseIndex3, menuIncreaseIndex4, menuDecreaseIndex4, menuIncreaseIndex5, menuDecreaseIndex5 }; // draw routines static void setGL( void ); static void setLighting( void ); static void setMeshMaterial( void ); static void callDisplayList( void ); static void updateDisplayList( void ); static void drawScene( void ); static void drawPolygons( void ); static void drawWireframe( void ); static void drawVectorField( void ); static void drawVertices( void ); static void drawSelectedVertices( void ); static void drawIsolatedVertices( void ); static void pickVertex(int x, int y); static void drawGenerators( void ); static void storeViewerState( void ); static void restoreViewerState( void ); static int windowSize[2]; static bool renderWireframe; // draw wireframe static bool renderVectorField; // draw vector field static bool renderGenerators; // draw generators static bool renderSelected; // draw picked vertices static Camera camera; // keeps track of view state static GLuint surfaceDL; // display list for mesh static Shader shader; // shader used to determine appearance of surface }; } #endif ================================================ FILE: Connection/obj/.empty ================================================ ================================================ FILE: Connection/shaders/fragment.glsl ================================================ uniform vec3 eye; uniform vec3 light; varying vec3 position; varying vec3 normal; float diffuse( vec3 N, vec3 L ) { return max( 0., dot( N, L )); } float specular( vec3 N, vec3 L, vec3 E ) { const float shininess = 8.; vec3 R = 2.*dot(L,N)*N - L; return pow( max( 0., dot( R, E )), shininess ); } float fresnel( vec3 N, vec3 E ) { const float sharpness = 10.; float NE = max( 0., dot( N, E )); return pow( sqrt( 1. - NE*NE ), sharpness ); } void main() { vec3 N = normalize( normal ); vec3 L = normalize( light - position ); vec3 E = normalize( eye - position ); vec3 R = 2.*dot(L,N)*N - L; vec3 one = vec3( 1., 1., 1. ); gl_FragColor.rgb = diffuse(N,L)*gl_Color.rgb + .5*specular(N,L,E)*one + .5*fresnel(N,E)*one; gl_FragColor.a = 1.; } ================================================ FILE: Connection/shaders/vertex.glsl ================================================ varying vec3 position; varying vec3 normal; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_FrontColor = gl_Color; position = gl_Vertex.xyz; normal = gl_Normal.xyz; } ================================================ FILE: Connection/src/Camera.cpp ================================================ #include #include #include using namespace std; #include "Camera.h" namespace DDG { Camera :: Camera( void ) : pClick( 1. ), pDrag( 1. ), pLast( 1. ), rLast( 1. ), momentum( 1. ), zoom( 1. ) {} Quaternion Camera :: clickToSphere( int x, int y ) { GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); int w = viewport[2]; int h = viewport[3]; Quaternion p( 0., 2. * (double) x / (double) w - 1., 2. * (double) y / (double) h - 1., 0. ); if( p.norm2() > 1. ) { p.normalize(); p.im().z = 0.; } else { p.im().z = sqrt( 1. - p.norm2() ); } return p; } Quaternion Camera :: currentRotation( void ) const { return ( pDrag * pClick.conj() ) * rLast; } void Camera :: setView( void ) const { Quaternion r = ( pDrag * pClick.conj() ) * rLast; double w = r[0]; double x = r[1]; double y = r[2]; double z = r[3]; GLdouble M[16] = { 1.-2.*y*y-2.*z*z, 2.*x*y+2.*w*z, 2.*x*z-2.*w*y, 0., 2.*x*y-2.*w*z, 1.-2.*x*x-2.*z*z, 2.*y*z+2.*w*x, 0., 2.*x*z+2.*w*y, 2.*y*z-2.*w*x, 1.-2.*x*x-2.*y*y, 0., 0., 0., 0., 1. }; glMatrixMode( GL_MODELVIEW ); glMultMatrixd( M ); } void Camera :: mouse( int button, int state, int x, int y ) { if( state == GLUT_DOWN ) { pClick = pDrag = pLast = clickToSphere( x, y ); momentum = 1.; } if( state == GLUT_UP ) { double timeSinceDrag = ( clock() - tLast ) / (double) CLOCKS_PER_SEC; if( timeSinceDrag < .1 ) { momentum = pDrag * pLast.conj(); momentum = ( .03 * momentum + .97 ).unit(); } else { momentum = 1.; } rLast = pDrag * pClick.conj() * rLast; pClick = pDrag = 1.; } } void Camera :: motion( int x, int y ) { tLast = clock(); pLast = pDrag; pDrag = clickToSphere( x, y ); } void Camera :: idle( void ) { // get time since last idle event static int t0 = clock(); int t1 = clock(); double dt = (t1-t0) / (double) CLOCKS_PER_SEC; rLast = momentum * rLast; momentum = ( (1.-.5*dt) * momentum + .5*dt ).unit(); zoom += vZoom*dt; vZoom *= max( 0., 1.-5.*dt ); t0 = t1; } void Camera :: zoomIn( void ) { vZoom -= 0.5; } void Camera :: zoomOut( void ) { vZoom += 0.5; } } ================================================ FILE: Connection/src/Complex.cpp ================================================ #include "Complex.h" #include #include using namespace std; namespace DDG { Complex::Complex( double a, double b ) // constructs number a+bi : re( a ), im( b ) {} void Complex::operator+=( const Complex& z ) // add z { re += z.re; im += z.im; } void Complex::operator-=( const Complex& z ) // subtract z { re -= z.re; im -= z.im; } void Complex::operator*=( const Complex& z ) // Complex multiply by z { double a = re; double b = im; double c = z.re; double d = z.im; re = a*c-b*d; im = a*d+b*c; } void Complex::operator*=( double r ) // scalar multiply by r { re *= r; im *= r; } void Complex::operator/=( double r ) // scalar divide by r { re /= r; im /= r; } void Complex::operator/=( const Complex& z ) // scalar divide by r { *this *= z.inv(); } Complex Complex::operator-( void ) const { return Complex( -re, -im ); } Complex Complex::conj( void ) const // returns Complex conjugate { return Complex( re, -im ); } Complex Complex::inv( void ) const // returns inverse { return this->conj() / this->norm2(); } double Complex::arg( void ) const // returns argument { return atan2( im, re ); } double Complex::norm( void ) const // returns norm { return sqrt( re*re + im*im ); } double Complex::norm2( void ) const // returns norm squared { return re*re + im*im; } Complex Complex::unit( void ) const // returns complex number with unit norm and same modulus { return *this / this->norm(); } Complex Complex::exponential( void ) const // complex exponentiation { return exp( re ) * Complex( cos( im ), sin( im )); } Complex operator+( const Complex& z1, const Complex& z2 ) // binary addition { Complex z = z1; z += z2; return z; } Complex operator-( const Complex& z1, const Complex& z2 ) // binary subtraction { Complex z = z1; z -= z2; return z; } Complex operator*( const Complex& z1, const Complex& z2 ) // binary Complex multiplication { Complex z = z1; z *= z2; return z; } Complex operator*( const Complex& z, double r ) // right scalar multiplication { Complex zr = z; zr *= r; return zr; } Complex operator*( double r, const Complex& z ) // left scalar multiplication { return z*r; } Complex operator/( const Complex& z, double r ) // scalar division { Complex zr = z; zr /= r; return zr; } Complex operator/( const Complex& z1, const Complex& z2 ) // complex division { Complex z = z1; z /= z2; return z; } double dot( const Complex& z1, const Complex& z2 ) { return z1.re*z2.re + z1.im*z2.im; } double cross( const Complex& z1, const Complex& z2 ) { return z1.re*z2.im - z1.im*z2.re; } std::ostream& operator<<( std::ostream& os, const Complex& z ) // prints components { if( z.im > 0 ) { os << z.re << " + " << z.im << "i"; } else if( z.im < 0 ) { os << z.re << " - " << -z.im << "i"; } else { os << z.re; } return os; } } ================================================ FILE: Connection/src/DenseMatrix.cpp ================================================ #include "DenseMatrix.h" namespace DDG { template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i] = data[i]; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_COMPLEX, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i*2+0] = data[i].re; x[i*2+1] = data[i].im; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { assert( nColumns() == 1 ); if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } cData = cholmod_l_allocate_dense( m*4, 1, m*4, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { x[i*4+k] = data[i][k]; } } return cData; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = x[i]; } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = Complex( x[i*2+0], x[i*2+1] ); } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); assert( B->ncol == 1 ); assert( B->nrow%4 == 0 ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow/4; n = 1; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m; i++ ) { data[i] = Quaternion( x[i*4+0], x[i*4+1], x[i*4+2], x[i*4+3] ); } return *this; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 3; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { double x = o(i,j); if( x == 0. ) { os << " 0"; for( int k = 0; k < p+6; k++ ) { os << " "; } } else if( x > 0. ) { os << " " << x << " "; } else { os << x << " "; } } os << "]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { Complex z = o(i,j); if( z.re == 0. ) { os << " 0"; for( int k = 0; k < p+5; k++ ) { os << " "; } } else if( z.re > 0. ) { os << " " << z.re; } else { os << z.re; } if( z.im == 0 ) { os << " "; } else if( z.im >= 0 ) { os << "+"; } else { os << "-"; } if( z.im == 0. ) { for( int k = 0; k < p+8; k++ ) { os << " "; } } else { os << abs( z.im ) << "i "; } } os << " ]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "["; for( int j = 0; j < o.nColumns(); j++ ) { Quaternion q = o(i,j); os << " " << q; } os << " ]" << endl; } return os; } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i] = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i].re = 2.*unitRand() - 1.; data[i].im = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { data[i][k] = 2.*unitRand() - 1.; } } } } ================================================ FILE: Connection/src/DenseMatrix.inl ================================================ #include #include #include #include using namespace std; #include "DenseMatrix.h" #include "LinearContext.h" #include "Quaternion.h" #include "SparseMatrix.h" #include "Utility.h" namespace DDG { extern LinearContext context; template DenseMatrix :: DenseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) { data.resize( m*n ); zero(); } template DenseMatrix :: DenseMatrix( const DenseMatrix& A ) // copy constructor : cData( NULL ) { *this = A; } template DenseMatrix :: ~DenseMatrix( void ) // destructor { if( cData != NULL ) { cholmod_l_free_dense( &cData, context ); } } template DenseMatrix DenseMatrix :: transpose( void ) const { const DenseMatrix& A( *this ); DenseMatrix AT( n, m ); for( int i = 0; i < n; i++ ) for( int j = 0; j < m; j++ ) { AT(i,j) = A(j,i).conj(); } return AT; } template SparseMatrix DenseMatrix::sparse( void ) // converts to a sparse matrix { SparseMatrix B; B = cholmod_l_dense_to_sparse( this->to_cholmod(), true, context ); return B; } template DenseMatrix DenseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); DenseMatrix AB( A.nRows(), B.nColumns() ); for( int i = 0; i < A.nRows(); i++ ) for( int j = 0; j < B.nColumns(); j++ ) for( int k = 0; k < A.nColumns(); k++ ) { AB( i, j ) += A( i, k ) * B( k, j ); } return AB; } template void DenseMatrix :: operator*=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c; } } template void DenseMatrix :: operator/=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c.inv(); } } template DenseMatrix DenseMatrix :: operator+( const DenseMatrix& B ) const // returns sum of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) + B(i,j); } return C; } template void DenseMatrix :: operator+=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) += B(i,j); } } template DenseMatrix DenseMatrix :: operator-( const DenseMatrix& B ) const // returns difference of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) - B(i,j); } return C; } template void DenseMatrix :: operator-=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) -= B(i,j); } } template DenseMatrix operator*( const T& c, const DenseMatrix& A ) { DenseMatrix cA = A; cA *= c; return cA; } template DenseMatrix operator*( const DenseMatrix& A, double c ) { return c*A; } template DenseMatrix operator/( const DenseMatrix& A, double c ) { DenseMatrix Ac = A; Ac /= c; return Ac; } template const DenseMatrix& DenseMatrix :: operator=( const DenseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template int DenseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int DenseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int DenseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void DenseMatrix :: zero( const T& val ) // sets all elements to val { for( int i = 0; i < m*n; i++ ) { data[i] = val; } } template double DenseMatrix :: norm( NormType type ) const { double r = 0.; if( type == lInfinity ) { for( int i = 0; i < m*n; i++ ) { r = max( r, data[i].norm() ); } } else if( type == lOne ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm(); } } else if( type == lTwo ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm2(); } r = sqrt( r ); } return r; } template void DenseMatrix :: normalize( void ) // divides by l2 norm { *this /= norm( lTwo ); } template T& DenseMatrix :: operator()( int row, int col ) { return data[row+m*col]; } template T DenseMatrix :: operator()( int row, int col ) const { return data[row+m*col]; } template T& DenseMatrix :: operator()( int index ) { return data[index]; } template T DenseMatrix :: operator()( int index ) const { return data[index]; } template T DenseMatrix::sum( void ) const // returns the sum of all entries { T total( 0., 0. ); for( int i = 0; i < m*n; i++ ) { total += data[i]; } return total; } template void DenseMatrix :: removeMean( void ) { T mean = 0.; int N = m*n; for( int i = 0; i < N; i++ ) { mean += data[i]; } mean /= (double) N; for( int i = 0; i < N; i++ ) { data[i] -= mean; } } template T dot( const DenseMatrix& x, const DenseMatrix& y ) // returns Euclidean inner product of x and y { return ( x.transpose() * y )(0); } template DenseMatrix DenseMatrix::operator-( void ) const // returns additive inverse of this matrix { const DenseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { B( i, j ) = -A( i, j ); } return B; } template T inner( const DenseMatrix& x, const DenseMatrix& y ) // standard inner product { T sum = 0.; assert( x.nRows() == y.nRows() && x.nColumns() == y.nColumns() ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * y(i); } return sum; } template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ) // inner product with respect a diagonal inner // product B represented as a dense vector { T sum = 0.; assert( x.nRows() == y.nRows() && x.nRows() == B.nRows() && x.nColumns() == 1 && B.nColumns() == 1 && y.nColumns() == 1 ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * B(i) * y(i); } return sum; } } ================================================ FILE: Connection/src/DiscreteExteriorCalculus.inl ================================================ #include "DiscreteExteriorCalculus.h" namespace DDG { template void HodgeStar0Form :: build( const Mesh& mesh, SparseMatrix& star0 ) // builds a diagonal matrix mapping primal discrete 0-forms // to dual discrete 2-forms { int nV = mesh.vertices.size(); star0 = SparseMatrix( nV, nV ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { int i = v->index; star0( i, i ) = v->area(); } } template void HodgeStar1Form :: build( const Mesh& mesh, SparseMatrix& star1 ) // builds a diagonal matrix mapping primal discrete 1-forms // to dual discrete 1-forms { int nE = mesh.edges.size(); star1 = SparseMatrix( nE, nE ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // get the cotangents of the two angles opposite this edge double cotAlpha = e->he->cotan(); double cotBeta = e->he->flip->cotan(); int i = e->index; star1( i, i ) = ( cotAlpha + cotBeta ) / 2.; } } template void HodgeStar2Form :: build( const Mesh& mesh, SparseMatrix& star2 ) // builds a diagonal matrix mapping primal discrete 2-forms // to dual discrete 2-forms { int nF = mesh.faces.size(); star2 = SparseMatrix( nF, nF ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { int i = f->index; star2( i, i ) = 1. / f->area(); } } template< class T > void ExteriorDerivative0Form :: build( const Mesh& mesh, SparseMatrix& d0 ) { int nV = mesh.vertices.size(); int nE = mesh.edges.size(); d0 = SparseMatrix( nE, nV ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // the row index is the index of the edge int r = e->index; // the column indices are the indices of the two // edge vertices -- orientation is determined by // the orientation of the edge's first half edge int ci = e->he->vertex->index; int cj = e->he->flip->vertex->index; d0( r, ci ) = -1.; d0( r, cj ) = 1.; } } template< class T > void ExteriorDerivative1Form :: build( const Mesh& mesh, SparseMatrix& d1 ) { int nE = mesh.edges.size(); int nF = mesh.faces.size(); d1 = SparseMatrix( nF, nE ); // visit each face for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { // the row index is the index of the face int r = f->index; // visit all edges of this face HalfEdgeCIter he = f->he; do { // the column index is the index of the current edge int c = he->edge->index; // relative orientation is determined by checking if // the current half edge is the first half edge of its // corresponding edge double s = ( he->edge->he == he ? 1. : -1. ); // set the entry for this edge d1( r, c ) = s; he = he->next; } while( he != f->he ); } } } ================================================ FILE: Connection/src/Edge.cpp ================================================ #include "Edge.h" #include "Mesh.h" namespace DDG { } ================================================ FILE: Connection/src/Face.cpp ================================================ #include "Face.h" #include "Mesh.h" #include "Vector.h" namespace DDG { double Face::area( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).norm() / 2.; } Vector Face::normal( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).unit(); } bool Face::isBoundary( void ) const { return he->onBoundary; } Vector Face :: circumcenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector n = he->rotatedEdge(); double h = 0.5*he->cotan(); return 0.5*(p0+p1) + h*n; } Vector Face :: barycenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return (p0 + p1 + p2)/3.; } } ================================================ FILE: Connection/src/HalfEdge.cpp ================================================ #include "HalfEdge.h" #include "Mesh.h" #include "Vector.h" #include "Quaternion.h" namespace DDG { double HalfEdge :: cotan( void ) const { if( onBoundary ) return 0.0; Vector p0 = next->next->vertex->position; Vector p1 = vertex->position; Vector p2 = next->vertex->position; Vector u = p1-p0; Vector v = p2-p0; return dot( u, v ) / cross( u, v ).norm(); } Vector HalfEdge :: rotatedEdge( void ) const { if( onBoundary ) return Vector(); Vector n = face->normal(); Vector p0 = vertex->position; Vector p1 = flip->vertex->position; return cross( n, p1-p0 ); } double HalfEdge :: angle( void ) const { if( onBoundary ) return 0.0; Vector p0 = next->next->vertex->position; Vector p1 = vertex->position; Vector p2 = next->vertex->position; Vector u = p1-p0; Vector v = p2-p0; return atan2( cross( u, v ).norm(), dot( u, v ) ); } } ================================================ FILE: Connection/src/Image.cpp ================================================ #include #include #include #include using namespace std; #include "Image.h" namespace DDG { Image :: Image( int width, int height ) : w( width ), h( height ), pixels( w*h*3 ) {} float& Image :: operator()( int x, int y ) // accesses pixel (x,y) { return pixels[ x + y*w ]; } const float& Image :: operator()( int x, int y ) const // accesses pixel (x,y) { return pixels[ x + y*w ]; } float Image :: sample( float x, float y ) const // samples image at (x,y) using bilinear filtering { const Image& I( *this ); float ax = x - floor( x ); float ay = y - floor( y ); float bx = 1. - ax; float by = 1. - ay; int x0 = (int) floor( x ); int y0 = (int) floor( y ); int x1 = x0 + 1; int y1 = y0 + 1; clamp( x0, y0 ); clamp( x1, y1 ); return by * ( bx * I(x0,y0) + ax * I(x1,y0) ) + ay * ( bx * I(x0,y1) + ax * I(x1,y1) ) ; } int Image :: width( void ) const // returns image width { return w; } int Image :: height( void ) const // returns image height { return h; } class TGAHeader // header format for Truevision TGA images { public: char idFieldSize; char colorMapType; char dataTypeCode; short colorMapOrigin; short colorMapLength; char colorMapEntrySize; short xOrigin; short yOrigin; short width; short height; char bitsPerPixel; char imageSpecification; }; void Image :: read( const char* filename ) // loads an image file in Truevision TGA format // (must be uncompressed RGB image with 24 or 32 bits per pixel) { ifstream in( filename, ios_base::binary ); if( !in.is_open() ) { cerr << "Error: could not open file " << filename << " for input!" << endl; exit( 1 ); } // read header TGAHeader header; in.read( (char*) &(header.idFieldSize), 1 ); in.read( (char*) &(header.colorMapType), 1 ); in.read( (char*) &(header.dataTypeCode), 1 ); in.read( (char*) &(header.colorMapOrigin), 2 ); in.read( (char*) &(header.colorMapLength), 2 ); in.read( (char*) &(header.colorMapEntrySize), 1 ); in.read( (char*) &(header.xOrigin), 2 ); in.read( (char*) &(header.yOrigin), 2 ); in.read( (char*) &(header.width), 2 ); in.read( (char*) &(header.height), 2 ); in.read( (char*) &(header.bitsPerPixel), 1 ); in.read( (char*) &(header.imageSpecification), 1 ); w = header.width; h = header.height; // validate data type const char uncompressedRGB = 2; if( header.dataTypeCode != uncompressedRGB || ( header.bitsPerPixel != 24 && header.bitsPerPixel != 32 )) { cerr << "Error: input must be uncompressed RGB image with 24 or 32 bits per pixel." << endl; exit( 1 ); } // read identification field (unused) vector idField( header.idFieldSize ); in.read( &idField[0], header.idFieldSize ); // read color map data (unused) if( header.colorMapType == 1 ) { int bytesPerEntry = header.colorMapEntrySize / 8; int colorMapSize = header.colorMapLength * bytesPerEntry; vector colorMapData( colorMapSize ); in.read( &colorMapData[0], colorMapSize ); } // read pixel data int n = w*h*header.bitsPerPixel/8; vector pixelData( n ); in.read( (char*) &pixelData[0], n ); // convert pixel data to floating point pixels.resize( n ); for( int i = 0; i < n; i++ ) { pixels[i] = (double) pixelData[i] / 255.; } } void Image :: write( const char* filename ) const // writes an image file in Truevision TGA format // (uncompressed RGB image with 24 bits per pixel) { ofstream out( filename, ios_base::binary ); if( !out.is_open() ) { cerr << "Error: could not open file " << filename << " for output!" << endl; exit( 1 ); } TGAHeader header; header.idFieldSize = 0; header.colorMapType = 0; header.dataTypeCode = 2; header.colorMapOrigin = 0; header.colorMapLength = 0; header.colorMapEntrySize = 0; header.xOrigin = 0; header.yOrigin = 0; header.width = w; header.height = h; header.bitsPerPixel = 24; header.imageSpecification = 0; // write header out.write( (char*) &(header.idFieldSize), 1 ); out.write( (char*) &(header.colorMapType), 1 ); out.write( (char*) &(header.dataTypeCode), 1 ); out.write( (char*) &(header.colorMapOrigin), 2 ); out.write( (char*) &(header.colorMapLength), 2 ); out.write( (char*) &(header.colorMapEntrySize), 1 ); out.write( (char*) &(header.xOrigin), 2 ); out.write( (char*) &(header.yOrigin), 2 ); out.write( (char*) &(header.width), 2 ); out.write( (char*) &(header.height), 2 ); out.write( (char*) &(header.bitsPerPixel), 1 ); out.write( (char*) &(header.imageSpecification), 1 ); // convert pixel data from floating point vector pixelData( w*h*3 ); for( int i = 0; i < w*h*3; i++ ) { pixelData[i] = (unsigned char)( pixels[i] * 255. ); } // write pixel data out.write( (char*) &pixelData[0], w*h*3 ); } void Image :: clamp( int& x, int& y ) const // clamps coordinates to range [0,w-1] x [0,h-1] { x = max( 0, min( w-1, x )); y = max( 0, min( h-1, y )); } } ================================================ FILE: Connection/src/LinearContext.cpp ================================================ #include "LinearContext.h" namespace DDG { // global context for linear solvers LinearContext context; LinearContext :: LinearContext( void ) // constructor { cholmod_l_start( &context ); } LinearContext :: ~LinearContext( void ) // destructor { cholmod_l_finish( &context ); } LinearContext :: operator cholmod_common*( void ) // allows LinearContext to be treated as a cholmod_common* { return &context; } } ================================================ FILE: Connection/src/LinearEquation.cpp ================================================ #include "LinearEquation.h" namespace DDG { LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ) // constructs a linear equation with the specified left- and right-hand side { LinearEquation eqn; eqn.lhs = lhs; eqn.rhs = rhs; return eqn; } } ================================================ FILE: Connection/src/LinearPolynomial.cpp ================================================ #include using namespace std; #include "LinearPolynomial.h" #include "Types.h" namespace DDG { LinearPolynomial :: LinearPolynomial( void ) : constantTerm( 0. ) {} LinearPolynomial :: LinearPolynomial( double c ) { *this = c; } LinearPolynomial :: LinearPolynomial( Variable& v ) { *this = v; } const LinearPolynomial& LinearPolynomial :: operator=( double c ) { linearTerms.clear(); constantTerm = c; return *this; } const LinearPolynomial& LinearPolynomial :: operator=( Variable& v ) { linearTerms.clear(); linearTerms[ &v ] = 1.; constantTerm = 0.; return *this; } void LinearPolynomial::operator+=( double c ) { constantTerm += c; } void LinearPolynomial::operator-=( double c ) { constantTerm -= c; } void LinearPolynomial::operator*=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second *= c; } constantTerm *= c; } void LinearPolynomial::operator/=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second /= c; } constantTerm /= c; } void LinearPolynomial::operator+=( Variable& v ) { LinearPolynomial p( v ); *this += p; } void LinearPolynomial::operator-=( Variable& v ) { LinearPolynomial p( v ); *this -= p; } void LinearPolynomial::operator+=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second += i->second; } else { linearTerms[i->first] = i->second; } } constantTerm += e.constantTerm; } void LinearPolynomial::operator-=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second -= i->second; } else { linearTerms[i->first] = -i->second; } } constantTerm -= e.constantTerm; } LinearPolynomial LinearPolynomial::operator-( void ) const { LinearPolynomial p = *this; for( TermIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { i->second = -i->second; } p.constantTerm = -p.constantTerm; return p; } double LinearPolynomial::evaluate( void ) const { double value = constantTerm; for( TermCIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { value += i->second * i->first->value; } return value; } LinearPolynomial operator+( double c, Variable& v ) { return c + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, double c ) { return LinearPolynomial(v) + c; } LinearPolynomial operator-( double c, Variable& v ) { return c - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, double c ) { return LinearPolynomial(v) - c; } LinearPolynomial operator*( double c, Variable& v ) { return LinearPolynomial(v) * c; } LinearPolynomial operator*( Variable& v, double c ) { return LinearPolynomial(v) * c; } LinearPolynomial operator/( Variable& v, double c ) { return LinearPolynomial(v) / c; } LinearPolynomial operator+( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) + LinearPolynomial(v2); } LinearPolynomial operator-( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) - LinearPolynomial(v2); } LinearPolynomial operator+( const LinearPolynomial& p, double c ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator+( double c, const LinearPolynomial& p ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, double c ) { LinearPolynomial difference = p; difference -= c; return difference; } LinearPolynomial operator-( double c, const LinearPolynomial& p ) { LinearPolynomial difference = -p; difference += c; return difference; } LinearPolynomial operator*( const LinearPolynomial& p, double c ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator*( double c, const LinearPolynomial& p ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator/( const LinearPolynomial& p, double c ) { LinearPolynomial quotient = p; quotient /= c; return quotient; } LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ) { return p + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) + p; } LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ) { return p - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) - p; } LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial sum = p; sum += q; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial difference = p; difference -= q; return difference; } ostream& operator<<( ostream& os, const LinearPolynomial& p ) { for( TermCIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { os << i->second << "*" << i->first->name << " + "; } os << p.constantTerm; return os; } } ================================================ FILE: Connection/src/LinearSystem.cpp ================================================ #include using namespace std; #include #include "LinearSystem.h" #include "LinearContext.h" #include "Types.h" namespace DDG { extern LinearContext context; void LinearSystem::clear( void ) // removes all equations from the system { equations.clear(); } void LinearSystem::push_back( const LinearEquation& e ) // appends the equation e to the sytem { equations.push_back( e ); } void LinearSystem::solve( void ) // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution { convertEquations(); indexVariables(); buildSparseMatrix(); buildRightHandSide(); computeSolution(); } void LinearSystem::convertEquations( void ) // converts each equation to its polynomial representation { currentEquations.clear(); for( EqnIter eqn = equations.begin(); eqn != equations.end(); eqn ++ ) { // move right-hand side to left-hand side LinearPolynomial p = eqn->lhs - eqn->rhs; // convert fixed variables to constants LinearPolynomial q( p.constantTerm ); for( TermIter t = p.linearTerms.begin(); t != p.linearTerms.end(); t ++ ) { const double& coefficient( t->second ); Variable& variable( *(t->first) ); // skip zeros if( coefficient == 0. ) continue; if( t->first->fixed ) { q += coefficient * variable.value; } else { q += coefficient * variable; } } if( q.linearTerms.size() > 0 ) { currentEquations.push_back( q ); } } nEquations = currentEquations.size(); } void LinearSystem::indexVariables( void ) // assign a unique index to each variable remaining in one of the polynomials { index.clear(); nVariables = 0; for( PolyCIter p = currentEquations.begin(); p != currentEquations.end(); p ++ ) { for( TermCIter t = p->linearTerms.begin(); t != p->linearTerms.end(); t ++ ) { IndexIter j = index.find( t->first ); // if we haven't seen this variable // before, assign it a unique index if( j == index.end() ) { index[ t->first ] = nVariables; nVariables++; } } } } void LinearSystem::buildSparseMatrix( void ) // build the sparse matrix representation of our current system { A = SparseMatrix( nEquations, nVariables ); for( int i = 0; i < nEquations; i++ ) { for( TermCIter t = currentEquations[i].linearTerms.begin(); t != currentEquations[i].linearTerms.end(); t ++ ) { int j = index[ t->first ]; A(i,j) = t->second; } } } void LinearSystem::buildRightHandSide( void ) // build the data vector for our current system { b = DenseMatrix( nEquations, 1 ); for( int i = 0; i < nEquations; i++ ) { b(i) = -currentEquations[i].constantTerm; } } void LinearSystem::computeSolution( void ) { // solve linear system Ax=b x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); // put solution values in variables for( IndexIter i = index.begin(); i != index.end(); i ++ ) { i->first->value = x( i->second ); } } } ================================================ FILE: Connection/src/Mesh.cpp ================================================ #include #include #include "Mesh.h" #include "MeshIO.h" #include "DiscreteExteriorCalculus.h" #include "Direction.h" #include "TreeCotree.h" #include "HarmonicBases.h" #include "Utility.h" using namespace std; namespace DDG { Mesh :: Mesh( void ) { firstGeneratorIndex = 0; } Mesh :: Mesh( const Mesh& mesh ) { *this = mesh; } class HalfEdgeIterCompare { public: bool operator()( const HalfEdgeIter& i, const HalfEdgeIter& j ) const { return &*i < &*j; } }; class HalfEdgeCIterCompare { public: bool operator()( const HalfEdgeCIter& i, const HalfEdgeCIter& j ) const { return &*i < &*j; } }; class VertexIterCompare { public: bool operator()( const VertexIter& i, const VertexIter& j ) const { return &*i < &*j; } }; class VertexCIterCompare { public: bool operator()( const VertexCIter& i, const VertexCIter& j ) const { return &*i < &*j; } }; class FaceIterCompare { public: bool operator()( const FaceIter& i, const FaceIter& j ) const { return &*i < &*j; } }; class FaceCIterCompare { public: bool operator()( const FaceCIter& i, const FaceCIter& j ) const { return &*i < &*j; } }; class EdgeIterCompare { public: bool operator()( const EdgeIter& i, const EdgeIter& j ) const { return &*i < &*j; } }; class EdgeCIterCompare { public: bool operator()( const EdgeCIter& i, const EdgeCIter& j ) const { return &*i < &*j; } }; const Mesh& Mesh :: operator=( const Mesh& mesh ) { map< HalfEdgeCIter, HalfEdgeIter, HalfEdgeCIterCompare > halfedgeOldToNew; map< VertexCIter, VertexIter, VertexCIterCompare > vertexOldToNew; map< EdgeCIter, EdgeIter, EdgeCIterCompare > edgeOldToNew; map< FaceCIter, FaceIter, FaceCIterCompare > faceOldToNew; // copy geometry from the original mesh and create a // map from pointers in the original mesh to // those in the new mesh halfedges.clear(); for( HalfEdgeCIter he = mesh.halfedges.begin(); he != mesh.halfedges.end(); he++ ) halfedgeOldToNew[ he ] = halfedges.insert( halfedges.end(), *he ); vertices.clear(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) vertexOldToNew[ v ] = vertices.insert( vertices.end(), *v ); edges.clear(); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e++ ) edgeOldToNew[ e ] = edges.insert( edges.end(), *e ); faces.clear(); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) faceOldToNew[ f ] = faces.insert( faces.end(), *f ); // "search and replace" old pointers with new ones for( HalfEdgeIter he = halfedges.begin(); he != halfedges.end(); he++ ) { he->next = halfedgeOldToNew[ he->next ]; he->flip = halfedgeOldToNew[ he->flip ]; he->vertex = vertexOldToNew[ he->vertex ]; he->edge = edgeOldToNew[ he->edge ]; he->face = faceOldToNew[ he->face ]; } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) v->he = halfedgeOldToNew[ v->he ]; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) e->he = halfedgeOldToNew[ e->he ]; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) f->he = halfedgeOldToNew[ f->he ]; return *this; } int Mesh::read( const string& filename ) { inputFilename = filename; ifstream in( filename.c_str() ); if( !in.is_open() ) { cerr << "Error reading from mesh file " << filename << endl; return 1; } int rval; if( !( rval = MeshIO::read( in, *this ))) { indexElements(); normalize(); } return rval; } int Mesh::write( const string& filename ) const // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error { ofstream out( filename.c_str() ); if( !out.is_open() ) { cerr << "Error writing to mesh file " << filename << endl; return 1; } MeshIO::write( out, *this ); return 0; } bool Mesh::reload( void ) { return read( inputFilename ); } void Mesh::normalize( void ) { // compute center of mass Vector c( 0., 0., 0. ); for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { c += v->position; } c /= (double) vertices.size(); // translate to origin for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position -= c; } // rescale such that the mesh sits inside the unit ball double rMax = 0.; for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { rMax = max( rMax, v->position.norm() ); } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position /= rMax; } } void Mesh::indexElements( void ) { int nV = 0; for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->index = nV; nV++; } int nE = 0; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) { e->index = nE; nE++; } int nF = 0; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) { f->index = nF; nF++; } } double Mesh::area( void ) const { double sum = 0.0; for( FaceCIter f = faces.begin(); f != faces.end(); f ++ ) { sum += f->area(); } return sum; } double Mesh::meanEdgeLength( void ) const { double sum = 0; for( EdgeCIter e = edges.begin(); e != edges.end(); e ++) { VertexIter v0 = e->he->vertex; VertexIter v1 = e->he->flip->vertex; sum += (v0->position - v1->position).norm(); } return sum / edges.size(); } int Mesh :: getEulerCharacteristicNumber( void ) const { return ( this->vertices.size() - this->edges.size() + this->faces.size() ); } bool Mesh :: isBoundaryGenerator(const Generator& cycle) const { if( cycle.size() == 0 ) return false; return ( cycle[0]->vertex->onBoundary() or cycle[0]->flip->vertex->onBoundary() ); } unsigned Mesh :: numberHarmonicBases() const { unsigned nb = 0; bool skipBoundaryLoop = true; for(unsigned i = 0; i < generators.size(); ++i) { const Generator& cycle = generators[i]; if( skipBoundaryLoop and isBoundaryGenerator(cycle) ) { skipBoundaryLoop = false; continue; } nb++; } return nb; } void Mesh :: faceFrame(HalfEdgeIter h, Vector& a, Vector& b) const { if( h->onBoundary ) return; VertexIter v0 = h->vertex; VertexIter v1 = h->next->vertex; a = ( v1->position - v0->position ).unit(); Vector n = h->face->normal(); b = cross( n, a ); } double Mesh :: parallelTransport(HalfEdgeIter h) const { if( h->onBoundary or h->flip->onBoundary ) return 0.0; VertexIter v0 = h->vertex; VertexIter v1 = h->next->vertex; Vector e = v1->position - v0->position; Vector aL, bL; faceFrame( h->face->he, aL, bL ); Vector aR, bR; faceFrame( h->flip->face->he, aR, bR ); double deltaL = atan2( dot(e,bL), dot(e,aL) ); double deltaR = atan2( dot(e,bR), dot(e,aR) ); return (deltaL - deltaR); } double Mesh :: connectionOneForm(HalfEdgeIter h) const { double angle = 0.0; // coclosed term double star1 = 0.5 * ( h->cotan() + h->flip->cotan() ); double u0 = h->flip->vertex->potential; double u1 = h->vertex->potential; angle += star1*(u1 - u0); // harmonic term for(unsigned k = 0; k < h->harmonicBases.size(); ++k) angle += this->harmonicCoefs[k] * h->harmonicBases[k]; return angle; } double Mesh :: vertexHolonomy(VertexIter vertex) const { double sum = 2.0*M_PI; HalfEdgeIter h = vertex->he; do { sum += parallelTransport(h); sum += connectionOneForm(h); h = h->flip->next; } while( h != vertex->he ); return sum; } double Mesh :: generatorHolonomy(const Generator& cycle) const { double sum = 0.0; if( cycle.empty() ) return sum; for(unsigned k = 0; k < cycle.size(); ++k) { HalfEdgeIter h = cycle[k]; sum += parallelTransport(h); sum += connectionOneForm(h); } while( sum < 0.0 ) sum += 2.0*M_PI; while( sum >= 2.0*M_PI ) sum -= 2.0*M_PI; return sum; } void Mesh :: init() { // Laplacian with Neumann boundary condition SparseMatrix star0, star1, d0, Delta; HodgeStar0Form::build( *this, star0 ); HodgeStar1Form::build( *this, star1 ); ExteriorDerivative0Form::build( *this, d0 ); Delta = d0.transpose() * star1 * d0; // make L positive-definite Delta += Real(1.0e-8)*star0; // pre-factorize this->L.build(Delta); // generators int t0 = clock(); TreeCotree tct; tct.build( *this ); int t1 = clock(); cout << "[generators] time: " << seconds( t0, t1 ) << "s" << "\n"; unsigned nb = this->numberHarmonicBases(); if( nb == 0 ) return; // harmonic coefs this->harmonicCoefs = std::vector( nb, 0.0 ); // harmonic bases t0 = clock(); HarmonicBases bases; bases.compute( *this ); t1 = clock(); cout << "[harmonic] time: " << seconds( t0, t1 ) << "s" << "\n"; } } ================================================ FILE: Connection/src/MeshIO.cpp ================================================ #include #include #include #include #include "MeshIO.h" #include "Mesh.h" using namespace std; namespace DDG { class Index { public: Index( void ) {} Index( int p, int t, int n ) : position( p ), texcoord( t ), normal( n ) {} bool operator<( const Index& i ) const { if( position < i.position ) return true; if( position > i.position ) return false; if( texcoord < i.texcoord ) return true; if( texcoord > i.texcoord ) return false; if( normal < i.normal ) return true; if( normal > i.normal ) return false; return false; } int position; int texcoord; int normal; }; class MeshData { public: std::vector positions; std::vector texcoords; std::vector normals; std::vector< std::vector< Index > > indices; }; int MeshIO :: read( istream& in, Mesh& mesh ) // reads a mesh from a valid, open input stream in { MeshData data; if( readMeshData( in, data )) { return 1; } if( buildMesh( data, mesh )) { return 1; } return 0; } void MeshIO :: write( ostream& out, const Mesh& mesh ) // writes a mesh to a valid, open output stream out { int currentIndex = 1; map vertexIndex; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) { out << "v " << v->position.x << " " << v->position.y << " " << v->position.z << endl; vertexIndex[ v ] = currentIndex; currentIndex++; } for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeIter he = f->he; for( int j = 0; j < 3; j++ ) { out << "vt " << he->texcoord.x << " " << he->texcoord.y << endl; he = he->next; } } for( size_t i = 0; i < mesh.faces.size(); i++ ) { const Face& f( mesh.faces[i] ); HalfEdgeIter he = f.he; out << "f "; int j = 0; do { out << vertexIndex[ he->vertex ] << "/" << 1+(i*3+j) << " "; he = he->next; j++; } while( he != f.he ); out << endl; } } int MeshIO :: readMeshData( istream& in, MeshData& data ) { string line; while( getline( in, line )) { stringstream ss( line ); string token; ss >> token; if( token == "v" ) { readPosition( ss, data ); continue; } // vertex if( token == "vt" ) { readTexCoord( ss, data ); continue; } // texture coordinate if( token == "vn" ) { readNormal ( ss, data ); continue; } // vertex normal if( token == "f" ) { readFace ( ss, data ); continue; } // face if( token[0] == '#' ) continue; // comment if( token == "o" ) continue; // object name if( token == "g" ) continue; // group name if( token == "s" ) continue; // smoothing group if( token == "mtllib" ) continue; // material library if( token == "usemtl" ) continue; // material if( token == "" ) continue; // empty string cerr << "Error: does not appear to be a valid Wavefront OBJ file!" << endl; cerr << "(Offending line: " << line << ")" << endl; return 1; } return 0; } void MeshIO :: preallocateMeshElements( const MeshData& data, Mesh& mesh ) { // count the number of edges set< pair > edges; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { for( unsigned int I = 0; I < f->size(); I++ ) { int J = (I+1) % f->size(); int i = (*f)[I].position; int j = (*f)[J].position; if( i > j ) swap( i, j ); edges.insert( pair( i, j )); } } int nV = data.positions.size(); int nE = edges.size(); int nF = data.indices.size(); int nHE = 2*nE; int chi = nV - nE + nF; int nB = max( 0, 2 - chi ); // (conservative approximation of number of boundary cycles) mesh.halfedges.clear(); mesh.vertices.clear(); mesh.edges.clear(); mesh.faces.clear(); mesh.boundaries.clear(); mesh.halfedges.reserve( nHE ); mesh.vertices.reserve( nV ); mesh.edges.reserve( nE ); mesh.faces.reserve( nF ); mesh.boundaries.reserve( nB ); } extern vector isolated; // all isolated vertices point to isolated.begin() int MeshIO :: buildMesh( const MeshData& data, Mesh& mesh ) { map< pair< int, int >, int > edgeCount; map< pair< int, int >, HalfEdgeIter > existingHalfEdges; map< int, VertexIter > indexToVertex; map< HalfEdgeIter, bool > hasFlipEdge; preallocateMeshElements( data, mesh ); // allocate a vertex for each position in the data and construct // a map from vertex indices to vertex pointers for( unsigned int i = 0; i < data.positions.size(); i++ ) { VertexIter newVertex = mesh.vertices.insert( mesh.vertices.end(), Vertex() ); newVertex->position = data.positions[ i ]; newVertex->he = isolated.begin(); indexToVertex[ i ] = newVertex; } // insert each face into the mesh int faceIndex = 0; bool degenerateFaces = false; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { int N = f->size(); // print an error if the face is degenerate if( N < 3 ) { cerr << "Error: face " << faceIndex << " is degenerate (fewer than three vertices)!" << endl; degenerateFaces = true; continue; } // create a new face FaceIter newFace = mesh.faces.insert( mesh.faces.end(), Face()); // create a new half edge for each edge of the current face vector< HalfEdgeIter > hes( N ); for( int i = 0; i < N; i++ ) { hes[ i ] = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); } // initialize these new halfedges for( int i = 0; i < N; i++ ) { // the current halfedge goes from vertex a to vertex b int a = (*f)[ i ].position; int b = (*f)[ (i+1) % N ].position; // set current halfedge's attributes hes[ i ]->next = hes[ (i+1) % N ]; hes[ i ]->vertex = indexToVertex[ a ]; int t = (*f)[i].texcoord; if( t >= 0 ) hes[ i ]->texcoord = data.texcoords[ t ]; else hes[ i ]->texcoord = Vector( 0., 0., 0. ); hes[ i ]->onBoundary = false; // keep track of which halfedges have flip edges defined (for detecting boundaries) hasFlipEdge[ hes[ i ]] = false; // point vertex a at the current halfedge indexToVertex[ a ]->he = hes[ i ]; // point the new face and this half edge to each-other hes[ i ]->face = newFace; newFace->he = hes[ i ]; // if we've created an edge between a and b in the past, it is the // flip edge of the current halfedge if( a > b ) swap( a, b ); if( existingHalfEdges.find( pair( a, b )) != existingHalfEdges.end()) { hes[ i ]->flip = existingHalfEdges[ pair( a, b ) ]; hes[ i ]->flip->flip = hes[ i ]; hes[ i ]->edge = hes[ i ]->flip->edge; hasFlipEdge[ hes[ i ]] = true; hasFlipEdge[ hes[ i ]->flip ] = true; } else // otherwise, create an edge connected to the current halfedge { hes[ i ]->edge = mesh.edges.insert( mesh.edges.end(), Edge()); hes[ i ]->edge->he = hes[i]; edgeCount[ pair( a, b ) ] = 0; } // record the fact that we've created a halfedge from a to b existingHalfEdges[ pair( a, b ) ] = hes[ i ]; // check for nonmanifold edges edgeCount[ pair( a, b ) ]++; if( edgeCount[ pair( a, b ) ] > 2 ) { cerr << "Error: edge (" << a << ", " << b << ") is nonmanifold (more than two faces sharing a single edge)!" << endl; return 1; } } faceIndex++; } // give up now if there were degenerate faces if( degenerateFaces ) { return 1; } // insert extra faces for each boundary cycle for( HalfEdgeIter currentHE = mesh.halfedges.begin(); currentHE != mesh.halfedges.end(); currentHE ++ ) { // if we find a halfedge with no flip edge defined, create // a new face and link it to the corresponding boundary cycle if( !hasFlipEdge[ currentHE ] ) { // create a new face FaceIter newBoundary = mesh.boundaries.insert( mesh.boundaries.end(), Face()); // walk along this boundary cycle vector boundaryCycle; HalfEdgeIter he = currentHE; do { // create a new halfedge on the boundary face HalfEdgeIter newHE = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); // mark only the halfedge on the boundary face as being on the boundary newHE->onBoundary = true; // link the current halfedge in the cycle to its new flip edge he->flip = newHE; // grab the next halfedge along the boundary by finding // the next halfedge around the current vertex that doesn't // have a flip edge defined HalfEdgeIter nextHE = he->next; while( hasFlipEdge[ nextHE ] ) { nextHE = nextHE->flip->next; } // set attributes for the flip edge (we'll set ->next below) newHE->flip = he; newHE->vertex = nextHE->vertex; newHE->edge = he->edge; newHE->face = newBoundary; newHE->texcoord = nextHE->texcoord; // point the new face to this half edge newBoundary->he = newHE; // keep track of all the new halfedges in the boundary cycle boundaryCycle.push_back( newHE ); // continue to walk along the cycle he = nextHE; } while( he != currentHE ); // link together the cycle of boundary halfedges unsigned int N = boundaryCycle.size(); for( unsigned int i = 0; i < N; i++ ) { boundaryCycle[ i ]->next = boundaryCycle[ (i+N-1)%N ]; hasFlipEdge[ boundaryCycle[i] ] = true; hasFlipEdge[ boundaryCycle[i]->flip ] = true; } } } // print a warning if the input has any non-terminal defects checkIsolatedVertices( mesh ); checkNonManifoldVertices( mesh ); return 0; } void MeshIO :: readPosition( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.positions.push_back( Vector( x, y, z )); } void MeshIO :: readTexCoord( stringstream& ss, MeshData& data ) { double u, v; ss >> u >> v; data.texcoords.push_back( Vector( u, v, 0. )); } void MeshIO :: readNormal( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.normals.push_back( Vector( x, y, z )); } void MeshIO :: readFace( stringstream& ss, MeshData &data ) { vector faceIndices; string token; while( ss >> token ) { faceIndices.push_back( parseFaceIndex( token )); } data.indices.push_back( faceIndices ); } Index MeshIO :: parseFaceIndex( const string& token ) { // parse indices of the form // // p/[t]/[n] // // where p is an index into positions, t is an index into // texcoords, n is an index into normals, and [.] indicates // that an index is optional stringstream in( token ); string indexstring; int indices[3] = { -1, -1, -1 }; int i = 0; while( getline( in, indexstring, '/' )) { stringstream ss( indexstring ); ss >> indices[i++]; } // decrement since indices in OBJ files are 1-based return Index( indices[0]-1, indices[1]-1, indices[2]-1 ); } void MeshIO :: checkIsolatedVertices( const Mesh& mesh ) { // print a warning if the mesh has any isolated vertices int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { cerr << "Warning: vertex " << vertexIndex << " is isolated (not contained in any face)." << endl; } vertexIndex++; } } void MeshIO :: checkNonManifoldVertices( const Mesh& mesh ) { map nIncidentFaces; for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } for( FaceCIter f = mesh.boundaries.begin(); f != mesh.boundaries.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( nIncidentFaces[v] != v->valence() ) { cerr << "Warning: vertex " << vertexIndex << " is nonmanifold." << endl; } vertexIndex++; } } } ================================================ FILE: Connection/src/Quaternion.cpp ================================================ #include #include using namespace std; #include "Quaternion.h" namespace DDG { // CONSTRUCTORS ---------------------------------------------------------- Quaternion :: Quaternion( void ) // initializes all components to zero : s( 0. ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Quaternion& q ) // initializes from existing quaternion : s( q.s ), v( q.v ) {} Quaternion :: Quaternion( double s_, double vi, double vj, double vk ) // initializes with specified double (s) and imaginary (v) components : s( s_ ), v( vi, vj, vk ) {} Quaternion :: Quaternion( double s_, const Vector& v_ ) // initializes with specified double(s) and imaginary (v) components : s( s_ ), v( v_ ) {} Quaternion :: Quaternion( double s_ ) // initializes purely real quaternion with specified real (s) component (imaginary part is zero) : s( s_ ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Vector& v_ ) // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) : s( 0. ), v( v_ ) {} Quaternion :: Quaternion( const Complex& z ) // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k : s( z.re ), v( z.im, 0., 0. ) {} // ASSIGNMENT OPERATORS -------------------------------------------------- const Quaternion& Quaternion :: operator=( double _s ) // assigns a purely real quaternion with real value s { s = _s; v = Vector( 0., 0., 0. ); return *this; } const Quaternion& Quaternion :: operator=( const Vector& _v ) // assigns a purely real quaternion with imaginary value v { s = 0.; v = _v; return *this; } // ACCESSORS ------------------------------------------------------------- double& Quaternion::operator[]( int index ) // returns reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } const double& Quaternion::operator[]( int index ) const // returns const reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } void Quaternion::toMatrix( double Q[4][4] ) const // returns 4x4 matrix representation { Q[0][0] = s; Q[0][1] = -v.x; Q[0][2] = -v.y; Q[0][3] = -v.z; Q[1][0] = v.x; Q[1][1] = s; Q[1][2] = -v.z; Q[1][3] = v.y; Q[2][0] = v.y; Q[2][1] = v.z; Q[2][2] = s; Q[2][3] = -v.x; Q[3][0] = v.z; Q[3][1] = -v.y; Q[3][2] = v.x; Q[3][3] = s; } double& Quaternion::re( void ) // returns reference to double part { return s; } const double& Quaternion::re( void ) const // returns const reference to double part { return s; } Vector& Quaternion::im( void ) // returns reference to imaginary part { return v; } const Vector& Quaternion::im( void ) const // returns const reference to imaginary part { return v; } // VECTOR SPACE OPERATIONS ----------------------------------------------- Quaternion Quaternion::operator+( const Quaternion& q ) const // addition { return Quaternion( s+q.s, v+q.v ); } Quaternion Quaternion::operator-( const Quaternion& q ) const // subtraction { return Quaternion( s-q.s, v-q.v ); } Quaternion Quaternion::operator-( void ) const // negation { return Quaternion( -s, -v ); } Quaternion Quaternion::operator*( double c ) const // scalar multiplication { return Quaternion( s*c, v*c ); } Quaternion operator*( double c, const Quaternion& q ) // scalar multiplication { return q*c; } Quaternion Quaternion::operator/( double c ) const // scalar division { return Quaternion( s/c, v/c ); } void Quaternion::operator+=( const Quaternion& q ) // addition / assignment { s += q.s; v += q.v; } void Quaternion::operator+=( double c ) // addition / assignment of pure real { s += c; } void Quaternion::operator-=( const Quaternion& q ) // subtraction / assignment { s -= q.s; v -= q.v; } void Quaternion::operator-=( double c ) // subtraction / assignment of pure real { s -= c; } void Quaternion::operator*=( double c ) // scalar multiplication / assignment { s *= c; v *= c; } void Quaternion::operator/=( double c ) // scalar division / assignment { s /= c; v /= c; } // ALGEBRAIC OPERATIONS -------------------------------------------------- Quaternion Quaternion::operator*( const Quaternion& q ) const // Hamilton product { const double& s1( s ); const double& s2( q.s ); const Vector& v1( v ); const Vector& v2( q.v ); return Quaternion( s1*s2 - dot(v1,v2), s1*v2 + s2*v1 + cross(v1,v2) ); } void Quaternion::operator*=( const Quaternion& q ) // Hamilton product / assignment { *this = ( *this * q ); } Quaternion Quaternion::conj( void ) const // conjugation { return Quaternion( s, -v ); } Quaternion Quaternion::inv( void ) const { return ( this->conj() ) / this->norm2(); } // NORMS ----------------------------------------------------------------- double Quaternion::norm( void ) const // returns Euclidean length { return sqrt( s*s + v.x*v.x + v.y*v.y + v.z*v.z ); } double Quaternion::norm2( void ) const // returns Euclidean length squared { return s*s + dot(v,v); } Quaternion Quaternion::unit( void ) const // returns unit quaternion { return *this / norm(); } void Quaternion::normalize( void ) // divides by Euclidean length { *this /= norm(); } // GEOMETRIC OPERATIONS -------------------------------------------------- Quaternion slerp( const Quaternion& q0, const Quaternion& q1, double t ) // spherical-linear interpolation { // interpolate length double m0 = q0.norm(); double m1 = q1.norm(); double m = (1-t)*m0 + t*m1; // interpolate direction Quaternion p0 = q0 / m0; Quaternion p1 = q1 / m1; double theta = acos(( p0.conj()*p1 ).re() ); Quaternion p = ( sin((1-t)*theta)*p0 + sin(t*theta)*p1 )/sin(theta); return m*p; } // I/O ------------------------------------------------------------------------- std::ostream& operator<<( std::ostream& os, const Quaternion& q ) // prints components { os << "( " << q.re() << ", " << q.im() << " )"; return os; } } ================================================ FILE: Connection/src/Real.cpp ================================================ #include "Real.h" #include namespace DDG { Real :: Real( double x ) // constructs real number with value x : value( x ) {} Real :: operator double( void ) const // type cast to double { return value; } void Real :: operator+=( double x ) // increment { value += x; } void Real :: operator-=( double x ) // decrement { value -= x; } void Real :: operator*=( double x ) // multiply { value *= x; } void Real :: operator/=( double x ) // divide { value /= x; } Real Real :: conj( void ) const // simply returns the value (for compatibility w/ complex numbers) { return value; } Real Real :: inv( void ) const // returns inverse { return 1. / value; } double Real :: norm( void ) const // returns norm { return fabs( value ); } double Real :: norm2( void ) const // returns norm squared { return value * value; } Real Real :: unit( void ) const // returns number with unit norm and same sign { return value / norm(); } } ================================================ FILE: Connection/src/Shader.cpp ================================================ #include "Shader.h" #include #include using namespace std; namespace DDG { Shader::Shader( void ) // constructor -- shader is initially invalid : vertexShader( 0 ), fragmentShader( 0 ), geometryShader( 0 ), program( 0 ), linked( false ) {} Shader::~Shader( void ) { if( program ) glDeleteProgram( program ); if( vertexShader ) glDeleteShader( vertexShader ); if( fragmentShader ) glDeleteShader( fragmentShader ); if( geometryShader ) glDeleteShader( geometryShader ); } void Shader::loadVertex( const char* filename ) { load( GL_VERTEX_SHADER, filename, vertexShader ); } void Shader::loadFragment( const char* filename ) { load( GL_FRAGMENT_SHADER, filename, fragmentShader ); } void Shader::loadGeometry( const char* filename ) { #ifdef GL_GEOMETRY_SHADER_EXT load( GL_GEOMETRY_SHADER_EXT, filename, geometryShader ); #else cerr << "Error: geometry shaders not supported!" << endl; #endif } void Shader::enable( void ) { if( !linked ) { glLinkProgram( program ); linked = true; } glUseProgram( program ); } void Shader::disable( void ) const { glUseProgram( 0 ); } Shader::operator GLuint( void ) const { return program; } void Shader::load( GLenum shaderType, const char* filename, GLuint& shader ) // read vertex shader from GLSL source file, compile, and attach to program { string source; if( !readSource( filename, source )) { return; } if( program == 0 ) { program = glCreateProgram(); } if( shader != 0 ) { glDetachShader( program, shader ); } shader = glCreateShader( shaderType ); const char* source_c_str = source.c_str(); glShaderSource( shader, 1, &(source_c_str), NULL ); glCompileShader( shader ); GLint compileStatus; glGetShaderiv( shader, GL_COMPILE_STATUS, &compileStatus ); if( compileStatus == GL_TRUE ) { glAttachShader( program, shader ); linked = false; } else { GLsizei maxLength = 0; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength ); if( maxLength > 0 ) { GLchar* infoLog = new char[ maxLength ]; GLsizei length; glGetShaderInfoLog( shader, maxLength, &length, infoLog ); cerr << "GLSL Error: " << infoLog << endl; delete[] infoLog; } } } bool Shader::readSource( const char* filename, std::string& source ) // reads GLSL source file into a string { source = ""; ifstream in( filename ); if( !in.is_open() ) { cerr << "Error: could not open shader file "; cerr << filename; cerr << " for input!" << endl; return false; } string line; while( getline( in, line )) { source += line; } return true; } } ================================================ FILE: Connection/src/SparseMatrix.cpp ================================================ #include "SparseMatrix.h" namespace DDG { template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = pr[k]; } } return *this; } template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = Complex( pr[k*2+0], pr[k*2+1] ); } } return *this; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ) { SparseMatrix A( m*4, n*4 ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; const Quaternion& q( e->second ); A(i*4+0,j*4+0) = q[0]; A(i*4+0,j*4+1) = -q[1]; A(i*4+0,j*4+2) = -q[2]; A(i*4+0,j*4+3) = -q[3]; A(i*4+1,j*4+0) = q[1]; A(i*4+1,j*4+1) = q[0]; A(i*4+1,j*4+2) = -q[3]; A(i*4+1,j*4+3) = q[2]; A(i*4+2,j*4+0) = q[2]; A(i*4+2,j*4+1) = q[3]; A(i*4+2,j*4+2) = q[0]; A(i*4+2,j*4+3) = -q[1]; A(i*4+3,j*4+0) = q[3]; A(i*4+3,j*4+1) = -q[2]; A(i*4+3,j*4+2) = q[1]; A(i*4+3,j*4+3) = q[0]; } if( cData != NULL ) { cholmod_l_free_sparse( &cData, context ); } cData = cholmod_l_copy_sparse( A.to_cholmod(), context ); return cData; } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_COMPLEX, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m*4, n*4, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i] = e->second; } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i*2+0] = e->second.re; pr[i*2+1] = e->second.im; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR< complex >( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (complex)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (quaternion)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4]/4 << "\n"; } template <> void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_zl_symbolic( n, n, Ap, Ai, Ax, NULL, &Symbolic, NULL, NULL ); umfpack_zl_numeric( Ap, Ai, Ax, NULL, Symbolic, &Numeric, NULL, NULL ); umfpack_zl_solve( UMFPACK_A, Ap, Ai, Ax, NULL, (double*) &x(0), NULL, (double*) &b(0), NULL, Numeric, NULL, NULL ); umfpack_zl_free_symbolic( &Symbolic ); umfpack_zl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } } ================================================ FILE: Connection/src/SparseMatrix.inl ================================================ #include #include #include #include #include using namespace std; #include #include #include "Real.h" #include "Complex.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "LinearContext.h" #include "Utility.h" namespace DDG { extern LinearContext context; const int maxEigIter = 20; // number of iterations used to solve eigenvalue problems template SparseMatrix :: SparseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) {} template SparseMatrix :: SparseMatrix( const SparseMatrix& B ) // copy constructor : cData( NULL ) { *this = B; } template SparseMatrix :: ~SparseMatrix( void ) // destructor { if( cData ) { cholmod_l_free_sparse( &cData, context ); } } template const SparseMatrix& SparseMatrix :: operator=( const SparseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template SparseMatrix SparseMatrix :: transpose( void ) const { SparseMatrix AT( n, m ); for( const_iterator e = begin(); e != end(); e++ ) { int i = e->first.second; int j = e->first.first; T Aij = e->second; AT(j,i) = Aij.conj(); } return AT; } template SparseMatrix SparseMatrix :: operator*( const SparseMatrix& B ) const // returns product of this matrix with sparse B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // collect nonzeros in each row vector< vector< int > > Bcol( B.nRows() ); vector< vector< T > > Bval( B.nRows() ); for( const_iterator e = B.begin(); e != B.end(); e ++ ) { int row = e->first.second; int col = e->first.first; T val = e->second; Bcol[ row ].push_back( col ); Bval[ row ].push_back( val ); } // multiply C = A*B SparseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( size_t n = 0; n < Bcol[j].size(); n++ ) { int k = Bcol[j][n]; C( i, k ) += e->second * Bval[j][n]; } } return C; } template DenseMatrix SparseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with dense B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // multiply C = A*B DenseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( int k = 0; k < B.nColumns(); k++ ) { C( i, k ) += e->second * B( j, k ); } } return C; } template void SparseMatrix :: operator*=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second *= c; } } template void SparseMatrix :: operator/=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second /= c; } } template void SparseMatrix :: operator+=( const SparseMatrix& B ) // adds B to this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) += Bij; } } template void SparseMatrix :: operator-=( const SparseMatrix& B ) // subtracts B from this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) -= Bij; } } template SparseMatrix SparseMatrix :: operator+( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C += B; return C; } template SparseMatrix SparseMatrix :: operator-( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C -= B; return C; } template SparseMatrix operator*( const T& c, const SparseMatrix& A ) { SparseMatrix cA = A; for( typename SparseMatrix::iterator e = cA.begin(); e != cA.end(); e++ ) { e->second = c * e->second; } return cA; } template SparseMatrix operator*( const SparseMatrix& A, const T& c ) { SparseMatrix Ac = A; Ac *= c; return Ac; } template SparseMatrix operator/( const SparseMatrix& A, T c ) { SparseMatrix Ac = A; Ac /= c; return Ac; } template void SparseMatrix :: resize( int m_, int n_ ) { m = m_; n = n_; data.clear(); } template int SparseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int SparseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int SparseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void SparseMatrix :: zero( const T& val ) // sets all nonzero elements val { for( iterator i = begin(); i != end(); i++ ) { i->second = val; } } template SparseMatrix SparseMatrix :: inverse( void ) const // returns inverse -- for diagonal matrices only { assert( m == n ); // matrix must be square const SparseMatrix& A( *this ); SparseMatrix Ainv( m, m ); for( const_iterator e = begin(); e != end(); e++ ) { int r = e->first.second; int c = e->first.first; assert( r == c ); // matrix must be diagonal Ainv( r, c ) = A( r, c ).inv(); } return Ainv; } template SparseMatrix SparseMatrix :: identity( int N ) { SparseMatrix I( N, N ); for( int i = 0; i < N; i++ ) { I( i, i ) = 1.; } return I; } template DenseMatrix SparseMatrix :: full( void ) const // converts to a dense matrix { const int maxSize = 1048576; if( m*n > maxSize ) { cerr << "Error: refusing to convert sparse to dense (too big!)" << "\n"; exit( 1 ); } const SparseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < m; j++ ) { B( i, j ) = A( i, j ); } return B; } template cholmod_sparse* SparseMatrix :: to_cholmod( void ) { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } allocateSparse(); // build compressed matrix (note that EntryMap stores entries in column-major order) double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; int i = 0; int j = -1; for( const_iterator e = begin(); e != end(); e ++ ) { int c = e->first.first; if( c != j ) { for( int k = j+1; k <= c; k++ ) { jc[k] = i; } j = c; } ir[i] = e->first.second; setEntry( e, i, pr ); i++; } for( int k = j+1; k <= n; k++ ) { jc[k] = i; } return cData; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ); template T& SparseMatrix :: operator()( int row, int col ) { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { data[ index ] = T( 0. ); } return data[ index ]; } template T SparseMatrix :: operator()( int row, int col ) const { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { return T( 0. ); } return entry->second; } template typename SparseMatrix::iterator SparseMatrix :: begin( void ) { return data.begin(); } template typename SparseMatrix::const_iterator SparseMatrix :: begin( void ) const { return data.begin(); } template typename SparseMatrix::iterator SparseMatrix :: end( void ) { return data.end(); } template typename SparseMatrix::const_iterator SparseMatrix :: end( void ) const { return data.end(); } template void SparseMatrix :: shift( double c ) // adds c times the identity matrix to this matrix { assert( m == n ); SparseMatrix& A( *this ); for( int i = 0; i < m; i++ ) { A( i, i ) += c; } } template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_dl_symbolic( n, n, Ap, Ai, Ax, &Symbolic, NULL, NULL ); umfpack_dl_numeric( Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL ); umfpack_dl_solve( UMFPACK_A, Ap, Ai, Ax, (double*) &x(0), (double*) &b(0), Numeric, NULL, NULL ); umfpack_dl_free_symbolic( &Symbolic ); umfpack_dl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; cholmod_factor* L = cholmod_l_analyze( Ac, context ); cholmod_l_factorize( Ac, L, context ); x = cholmod_l_solve( CHOLMOD_A, L, b.to_cholmod(), context ); if( L ) cholmod_l_free_factor( &L, context ); int t1 = clock(); cout << "[chol] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[chol] max residual: " << residual( A, x, b ) << "\n"; } template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ) // backsolves the prefactored positive definite sparse linear system LL'x = b { x = cholmod_l_solve( CHOLMOD_A, L.to_cholmod(), b.to_cholmod(), context ); } template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess { int t0 = clock(); for( int iter = 0; iter < maxEigIter; iter++ ) { solve( A, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess { // TODO use a symmetric matrix decomposition instead of QR int t0 = clock(); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= dot( e, B*e ).norm(); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; solve( A, x, x ); x -= dot( x, Be ).conj()*e; x /= dot( x, B*x ).norm(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); for( int iter = 0; iter < maxEigIter; iter++ ) { backsolvePositiveDefinite( L, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= sqrt( dot( e, B*e ).norm() ); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; backsolvePositiveDefinite( L, x, x ); x -= dot( x, Be ).conj()*e; x /= sqrt( dot( x, B*x ).norm() ); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ) // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess { int iter; int t0 = clock(); DenseMatrix ET = E.transpose(); SparseFactor L; L.build( A ); for( iter = 0; iter < maxEigIter; iter++ ) { x = B*x - E*(ET*x); backsolvePositiveDefinite( L, x, x ); x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, E, x ) << "\n"; } template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ) // returns the max residual of the linear problem A x = b relative to the largest entry of the solution { return ( A*x - b ).norm() / b.norm(); } template double residual( const SparseMatrix& A, const DenseMatrix& x ) // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, x ); return (A*x-lambda*x).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, x ); return (A*x-lambda*(B*x)).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, E, x ); return (A*x-lambda*(B*x-E*(E.transpose()*x))).norm() / x.norm(); } template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*x)(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x))(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns /<(B-EE^T)x,x> { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x-E*(E.transpose()*x)))(0).inv(); } template std::ostream& operator<<( std::ostream& os, const SparseMatrix& o) { os.precision( 3 ); for( typename SparseMatrix::const_iterator e = o.begin(); e != o.end(); e ++ ) { int row = e->first.second; int col = e->first.first; os << "( " << row << ", " << col << " ): " << e->second << "\n"; } return os; } template SparseFactor :: SparseFactor( void ) : L( NULL ) {} template SparseFactor :: ~SparseFactor( void ) { if( L ) { cholmod_l_free_factor( &L, context ); } } template void SparseFactor :: build( SparseMatrix& A ) { if( L ) { cholmod_l_free_factor( &L, context ); L = NULL; } int t0, t1; cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; t0 = clock(); L = cholmod_l_analyze( Ac, context ); t1 = clock(); cerr << "analyze: " << seconds(t0,t1) << "s" << endl; t0 = clock(); cholmod_l_factorize( Ac, L, context ); t1 = clock(); cerr << "factorize: " << seconds(t0,t1) << "s" << endl; } template bool SparseFactor :: valid( void ) const { if( L == NULL ) { return false; } return true; } template cholmod_factor* SparseFactor :: to_cholmod( void ) { return L; } } ================================================ FILE: Connection/src/Variable.cpp ================================================ #include "Variable.h" namespace DDG { Variable :: Variable( double value_, bool fixed_ ) // initialize a variable which has value zero and is not fixed by default : value( value_ ), fixed( fixed_ ) {} Variable :: Variable( std::string name_, double value_, bool fixed_ ) // initialize a named variable which has value zero and is not fixed by default : name( name_ ), value( value_ ), fixed( fixed_ ) {} double& Variable :: operator*( void ) // returns a reference to the numerical value { return value; } const double& Variable :: operator*( void ) const // returns a const reference to the numerical value { return value; } } ================================================ FILE: Connection/src/Vector.cpp ================================================ #include #include "Vector.h" namespace DDG { Vector :: Vector( void ) : x( 0. ), y( 0. ), z( 0. ) {} Vector :: Vector( double x0, double y0, double z0 ) : x( x0 ), y( y0 ), z( z0 ) {} Vector :: Vector( const Vector& v ) : x( v.x ), y( v.y ), z( v.z ) {} double& Vector :: operator[]( const int& index ) { return ( &x )[ index ]; } const double& Vector :: operator[]( const int& index ) const { return ( &x )[ index ]; } Vector Vector :: operator+( const Vector& v ) const { return Vector( x + v.x, y + v.y, z + v.z ); } Vector Vector :: operator-( const Vector& v ) const { return Vector( x - v.x, y - v.y, z - v.z ); } Vector Vector :: operator-( void ) const { return Vector( -x, -y, -z ); } Vector Vector :: operator*( const double& c ) const { return Vector( x*c, y*c, z*c ); } Vector operator*( const double& c, const Vector& v ) { return v*c; } Vector Vector :: operator/( const double& c ) const { return (*this) * ( 1./c ); } void Vector :: operator+=( const Vector& v ) { x += v.x; y += v.y; z += v.z; } void Vector :: operator-=( const Vector& v ) { x -= v.x; y -= v.y; z -= v.z; } void Vector :: operator*=( const double& c ) { x *= c; y *= c; z *= c; } void Vector :: operator/=( const double& c ) { (*this) *= ( 1./c ); } double Vector :: norm( void ) const { return sqrt( norm2()); } double Vector :: norm2( void ) const { return dot( *this, *this ); } void Vector :: normalize( void ) { (*this) /= norm(); } Vector Vector :: unit( void ) const { return (*this) / norm(); } Vector Vector :: abs( void ) const { return Vector( fabs( x ), fabs( y ), fabs( z ) ); } double dot( const Vector& u, const Vector& v ) { return u.x*v.x + u.y*v.y + u.z*v.z ; } Vector cross( const Vector& u, const Vector& v ) { return Vector( u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x ); } std::ostream& operator << (std::ostream& os, const Vector& o) { os << "[ " << o.x << " " << o.y << " " << o.z << " ]"; return os; } } ================================================ FILE: Connection/src/Vertex.cpp ================================================ #include using namespace std; #include "Vertex.h" #include "Mesh.h" #include "HalfEdge.h" namespace DDG { double Vertex::area( void ) const // returns the dual area associated with this vertex { double A = 0.; HalfEdgeCIter h = he; do { if (not h->onBoundary) A += h->face->area(); h = h->flip->next; } while( h != he ); return A / 3.; } Vector Vertex::normal( void ) const // returns the vertex normal { Vector N; HalfEdgeCIter h = he; do { if (not h->onBoundary) N += h->face->normal(); h = h->flip->next; } while( h != he ); return N.unit(); } vector isolated; // all isolated vertices point to isolated.begin() bool Vertex::isIsolated( void ) const // returns true if the vertex is not contained in any face or edge; false otherwise { return he == isolated.begin(); } int Vertex :: valence( void ) const // returns the number of incident faces { int n = 0; HalfEdgeCIter h = he; do { n++; h = h->flip->next; } while( h != he ); return n; } void Vertex :: toggleTag() { tag = !tag; } double Vertex :: theta( void ) const { double sum = 0.0; HalfEdgeCIter h = this->he; do { sum += h->next->angle(); h = h->flip->next; } while( h != this->he ); return sum; } bool Vertex :: onBoundary( void ) const { HalfEdgeCIter h = this->he; do { if( h->onBoundary ) return true; h = h->flip->next; } while( h != this->he ); return false; } } ================================================ FILE: Connection/src/Viewer.cpp ================================================ #include #include #include #include #include #include #include using namespace std; #include "Viewer.h" #include "Image.h" #include "Application.h" #include "Direction.h" #define AGE_SIZE 5 namespace DDG { // declare static member variables Mesh Viewer::mesh; GLuint Viewer::surfaceDL = 0; int Viewer::windowSize[2] = { 512, 512 }; Camera Viewer::camera; Shader Viewer::shader; bool Viewer::renderWireframe = true; bool Viewer::renderVectorField = true; bool Viewer::renderGenerators = true; bool Viewer::renderSelected = true; double Viewer::angle = 0.0; double Viewer::increment = 0.1; unsigned Viewer::oldest = 0; unsigned Viewer::counter = 0; std::vector Viewer::indices; std::vector Viewer::age = std::vector(AGE_SIZE, 0); void Viewer :: init( void ) { restoreViewerState(); initGLUT(); setGL(); initGLSL(); updateDisplayList(); glutMainLoop(); } void Viewer :: initGLUT( void ) { int argc = 0; vector< vector > argv(1); // initialize window glutInitWindowSize( windowSize[0], windowSize[1] ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); glutInit( &argc, (char**)&argv ); glutCreateWindow( "DDG" ); // specify callbacks glutDisplayFunc ( Viewer::display ); glutIdleFunc ( Viewer::idle ); glutKeyboardFunc ( Viewer::keyboard ); glutSpecialFunc ( Viewer::special ); glutMouseFunc ( Viewer::mouse ); glutMotionFunc ( Viewer::motion ); // initialize menus int viewMenu = glutCreateMenu( Viewer::view ); glutSetMenu( viewMenu ); glutAddMenuEntry( "[f] Wireframe", menuWireframe ); glutAddMenuEntry( "[↑] Zoom In", menuZoomIn ); glutAddMenuEntry( "[↓] Zoom Out", menuZoomOut ); glutAddMenuEntry( "[-] DecreaseAngle", menuDecreaseAngle ); glutAddMenuEntry( "[=] IncreaseAngle", menuIncreaseAngle ); glutAddMenuEntry( "[v] VectorField", menuVectorField ); glutAddMenuEntry( "[g] Generators", menuGenerators ); glutAddMenuEntry( "[s] Selected", menuSelected ); glutAddMenuEntry( "[0] IncreaseIndex0", menuIncreaseIndex0 ); glutAddMenuEntry( "[)] DecreaseIndex0", menuDecreaseIndex0 ); glutAddMenuEntry( "[1] IncreaseIndex1", menuIncreaseIndex1 ); glutAddMenuEntry( "[!] DecreaseIndex1", menuDecreaseIndex1 ); glutAddMenuEntry( "[2] IncreaseIndex2", menuIncreaseIndex2 ); glutAddMenuEntry( "[@] DecreaseIndex2", menuDecreaseIndex2 ); glutAddMenuEntry( "[3] IncreaseIndex3", menuIncreaseIndex3 ); glutAddMenuEntry( "[#] DecreaseIndex3", menuDecreaseIndex3 ); glutAddMenuEntry( "[4] IncreaseIndex4", menuIncreaseIndex4 ); glutAddMenuEntry( "[$] DecreaseIndex4", menuDecreaseIndex4 ); glutAddMenuEntry( "[5] IncreaseIndex5", menuIncreaseIndex5 ); glutAddMenuEntry( "[%] DecreaseIndex5", menuDecreaseIndex5 ); int mainMenu = glutCreateMenu( Viewer::menu ); glutSetMenu( mainMenu ); glutAddMenuEntry( "[space] Process Mesh", menuProcess ); glutAddMenuEntry( "[r] Reset Mesh", menuResetMesh ); glutAddMenuEntry( "[w] Write Mesh", menuWriteMesh ); glutAddMenuEntry( "[\\] Screenshot", menuScreenshot ); glutAddMenuEntry( "[esc] Exit", menuExit ); glutAddSubMenu( "View", viewMenu ); glutAttachMenu( GLUT_RIGHT_BUTTON ); } void Viewer :: initGLSL( void ) { shader.loadVertex( "shaders/vertex.glsl" ); shader.loadFragment( "shaders/fragment.glsl" ); } void Viewer :: menu( int value ) { switch( value ) { case( menuProcess ): mProcess(); break; case( menuResetMesh ): mResetMesh(); break; case( menuWriteMesh ): mWriteMesh(); break; case( menuScreenshot ): mScreenshot(); break; case( menuExit ): mExit(); break; case( menuIncreaseAngle ): mIncreaseAngle(); break; case( menuDecreaseAngle ): mDecreaseAngle(); break; case( menuIncreaseIndex0 ): mIncreaseIndexG(); break; case( menuDecreaseIndex0 ): mDecreaseIndexG(); break; case( menuIncreaseIndex1 ): mIncreaseIndexV(1); break; case( menuDecreaseIndex1 ): mDecreaseIndexV(1); break; case( menuIncreaseIndex2 ): mIncreaseIndexV(2); break; case( menuDecreaseIndex2 ): mDecreaseIndexV(2); break; case( menuIncreaseIndex3 ): mIncreaseIndexV(3); break; case( menuDecreaseIndex3 ): mDecreaseIndexV(3); break; case( menuIncreaseIndex4 ): mIncreaseIndexV(4); break; case( menuDecreaseIndex4 ): mDecreaseIndexV(4); break; case( menuIncreaseIndex5 ): mIncreaseIndexV(5); break; case( menuDecreaseIndex5 ): mDecreaseIndexV(5); break; default: break; } } void Viewer :: view( int value ) { switch( value ) { case( menuWireframe ): mWireframe(); break; case( menuZoomIn ): mZoomIn(); break; case( menuZoomOut ): mZoomOut(); break; case( menuVectorField ): mVectorField(); break; case( menuGenerators ): mGenerators(); break; case( menuSelected ): mSelected(); break; default: break; } } void Viewer :: keyboard( unsigned char c, int x, int y ) { switch( c ) { case 'f': mWireframe(); break; case 'w': mWriteMesh(); break; case 'r': mResetMesh(); break; case '\\': mScreenshot(); break; case ' ': mProcess(); break; case 27: mExit(); break; case '-': mDecreaseAngle(); break; case '=': mIncreaseAngle(); break; case 'v': mVectorField(); break; case 'g': mGenerators(); break; case 's': mSelected(); break; case '0': mIncreaseIndexG(); break; case ')': mDecreaseIndexG(); break; case '1': mIncreaseIndexV(0); break; case '!': mDecreaseIndexV(0); break; case '2': mIncreaseIndexV(1); break; case '@': mDecreaseIndexV(1); break; case '3': mIncreaseIndexV(2); break; case '#': mDecreaseIndexV(2); break; case '4': mIncreaseIndexV(3); break; case '$': mDecreaseIndexV(3); break; case '5': mIncreaseIndexV(4); break; case '%': mDecreaseIndexV(4); break; default: break; } } void Viewer :: special( int i, int x, int y ) { switch( i ) { case GLUT_KEY_UP: camera.zoomIn(); break; case GLUT_KEY_DOWN: camera.zoomOut(); break; case 27: mExit(); break; default: break; } } void Viewer :: mouse( int button, int state, int x, int y ) { if( ( glutGetModifiers() & GLUT_ACTIVE_SHIFT) and state == GLUT_UP ) pickVertex(x, y); else camera.mouse( button, state, x, y ); } void Viewer :: motion( int x, int y ) { camera.motion( x, y ); } void Viewer :: idle( void ) { camera.idle(); glutPostRedisplay(); } void Viewer :: storeViewerState( void ) { ofstream out( ".viewer_state.txt" ); out << camera.rLast[0] << endl; out << camera.rLast[1] << endl; out << camera.rLast[2] << endl; out << camera.rLast[3] << endl; GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); out << view[2] << endl; out << view[3] << endl; } void Viewer :: restoreViewerState( void ) { ifstream in( ".viewer_state.txt" ); if( !in.is_open() ) return; in >> camera.rLast[0]; in >> camera.rLast[1]; in >> camera.rLast[2]; in >> camera.rLast[3]; in >> windowSize[0]; in >> windowSize[1]; } void Viewer :: mIncreaseIndexG( void ) { if( mesh.generators.empty() ) return; mesh.firstGeneratorIndex++; std::cout << "SingG0: " << mesh.firstGeneratorIndex << std::endl; } void Viewer :: mDecreaseIndexG( void ) { if( mesh.generators.empty() ) return; mesh.firstGeneratorIndex--; std::cout << "SingG0: " << mesh.firstGeneratorIndex << std::endl; } void Viewer :: mIncreaseIndexV( int i ) { if( int(indices.size()) < i) return; updateIndex(i, true); } void Viewer :: mDecreaseIndexV( int i ) { if( int(indices.size()) < i) return; updateIndex(i, false); } void Viewer :: updateIndex( int i, bool increment ) { if( increment ) mesh.vertices[ indices[i] ].singularity++; else mesh.vertices[ indices[i] ].singularity--; std::cout << "SingV" << indices[i] << ": " << mesh.vertices[ indices[i] ].singularity << std::endl; counter++; age[i] = counter; computeOldest(); normalizeSingularities(); } void Viewer :: computeOldest( void ) { if( indices.empty() ) return; unsigned oldest_age = counter + 1; for( unsigned i = 0; i < indices.size(); ++i) { if( age[i] < oldest_age ) { oldest_age = age[i]; oldest = i; } } } void Viewer :: normalizeSingularities( void ) { if( indices.empty() ) return; double sing = mesh.firstGeneratorIndex; for( unsigned i = 0; i < indices.size(); ++i) sing += mesh.vertices[ indices[i] ].singularity; double chi = mesh.getEulerCharacteristicNumber(); mesh.vertices[ indices[oldest] ].singularity += (chi - sing); std::cout << "OldestV" << indices[oldest] << ": " << mesh.vertices[ indices[oldest] ].singularity << std::endl; } void Viewer :: mIncreaseAngle( void ) { angle += increment; DirectionField field; field.generate( mesh, angle ); updateDisplayList(); std::cout << "Angle = " << angle << std::endl; } void Viewer :: mDecreaseAngle( void ) { angle -= increment; DirectionField field; field.generate( mesh, angle ); updateDisplayList(); std::cout << "Angle = " << angle << std::endl; } void Viewer :: mProcess( void ) { Application app; bool ok = app.solveForConneciton(mesh); if( ok ) { DirectionField field; field.generate( mesh, angle ); } updateDisplayList(); } void Viewer :: mSelected( void ) { renderSelected = !renderSelected; updateDisplayList(); } void Viewer :: mResetMesh( void ) { mesh.reload(); mesh.init(); DirectionField field; field.generate( mesh, angle ); oldest = 0; counter = 0; indices.clear(); age = std::vector(AGE_SIZE,0); updateDisplayList(); } void Viewer :: mWriteMesh( void ) { mesh.write( "out.obj" ); } void Viewer :: mExit( void ) { //storeViewerState(); exit( 0 ); } void Viewer :: mWireframe( void ) { renderWireframe = !renderWireframe; updateDisplayList(); } void Viewer :: mVectorField( void ) { renderVectorField = !renderVectorField; updateDisplayList(); } void Viewer :: mGenerators( void ) { renderGenerators = !renderGenerators; updateDisplayList(); } void Viewer :: mZoomIn( void ) { camera.zoomIn(); } void Viewer :: mZoomOut( void ) { camera.zoomOut(); } void Viewer :: mScreenshot( void ) { static int index = 0; // get window width and height GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); int w = view[2]; int h = view[3]; // get pixels Image image( w, h ); glReadPixels( 0, 0, w, h, GL_BGR, GL_FLOAT, &image(0,0) ); stringstream filename; filename << "frames/viewer" << setw(8) << setfill( '0' ) << index << ".tga"; image.write( filename.str().c_str() ); index++; } void Viewer :: display( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); shader.enable(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); double aspect = (double) viewport[2] / (double) viewport[3]; const double fovy = 50.; const double clipNear = .01; const double clipFar = 1000.; gluPerspective( fovy, aspect, clipNear, clipFar ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); Quaternion eye = Vector( 0., 0., -2.5*camera.zoom ); Quaternion center = Vector( 0., 0., 0. ); Quaternion up = Vector( 0., 1., 0. ); gluLookAt( eye[1], eye[2], eye[3], center[1], center[2], center[3], up[1], up[2], up[3] ); Quaternion r = camera.currentRotation(); eye = r.conj() * eye * r; GLint uniformEye = glGetUniformLocation( shader, "eye" ); glUniform3f( uniformEye, eye[1], eye[2], eye[3] ); Quaternion light = Vector( -1., 1., -2. ); light = r.conj() * light * r; GLint uniformLight = glGetUniformLocation( shader, "light" ); glUniform3f( uniformLight, light[1], light[2], light[3] ); camera.setView(); callDisplayList(); shader.disable(); glutSwapBuffers(); } void Viewer :: updateDisplayList( void ) { if( surfaceDL ) { glDeleteLists( surfaceDL, 1 ); surfaceDL = 0; } surfaceDL = glGenLists( 1 ); glNewList( surfaceDL, GL_COMPILE ); setMeshMaterial(); drawScene(); glEndList(); } void Viewer :: setGL( void ) { glClearColor( .5, .5, .5, 1. ); setLighting(); } void Viewer :: setLighting( void ) { GLfloat position[4] = { 20., 30., 40., 0. }; glLightfv( GL_LIGHT0, GL_POSITION, position ); glEnable( GL_LIGHT0 ); glEnable( GL_NORMALIZE ); } void Viewer :: setMeshMaterial( void ) { GLfloat diffuse[4] = { .8, .5, .3, 1. }; GLfloat specular[4] = { .3, .3, .3, 1. }; GLfloat ambient[4] = { .2, .2, .5, 1. }; glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular ); glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient ); glMaterialf ( GL_FRONT_AND_BACK, GL_SHININESS, 16. ); } void Viewer :: callDisplayList( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_DEPTH_TEST ); glEnable( GL_LIGHTING ); glCallList( surfaceDL ); glPopAttrib(); } void Viewer :: drawScene( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1., 1. ); glColor3d( 1., .5, .25 ); drawPolygons(); glDisable( GL_POLYGON_OFFSET_FILL ); if( renderWireframe ) drawWireframe(); if( renderVectorField ) drawVectorField(); if( renderGenerators ) drawGenerators(); drawIsolatedVertices(); if( renderSelected ) drawSelectedVertices(); glPopAttrib(); } void Viewer :: drawPolygons( void ) { glEnable(GL_COLOR_MATERIAL); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { if( f->isBoundary() ) continue; glBegin( GL_POLYGON ); if( renderWireframe ) { Vector N = f->normal(); glNormal3dv( &N[0] ); } HalfEdgeCIter he = f->he; do { if( not renderWireframe ) { Vector N = he->vertex->normal(); glNormal3dv( &N[0] ); } glVertex3dv( &he->vertex->position[0] ); he = he->next; } while( he != f->he ); glEnd(); } glDisable(GL_COLOR_MATERIAL); } void Viewer :: drawWireframe( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glDisable( GL_LIGHTING ); glColor4f( 0., 0., 0., 0.5 ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBegin( GL_LINES ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { glVertex3dv( &e->he->vertex->position[0] ); glVertex3dv( &e->he->flip->vertex->position[0] ); } glEnd(); glPopAttrib(); } void Viewer :: drawIsolatedVertices( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glPointSize( 5 ); glHint( GL_POINT_SMOOTH_HINT, GL_NICEST ); glEnable( GL_POINT_SMOOTH ); glColor3f( 1., 0., 0. ); glBegin( GL_POINTS ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { glVertex3dv( &v->position[0] ); } } glEnd(); glPopAttrib(); } void Viewer :: drawVertices( void ) { for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { glLoadName(v->index); glBegin(GL_POINTS); glVertex3dv( &v->position[0] ); glEnd(); } } void Viewer :: drawSelectedVertices( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_COLOR_MATERIAL ); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glColor3f( 0., 0.5, 0.5 ); double h = 0.5*mesh.meanEdgeLength(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->tag ) { glPushMatrix(); glTranslated(v->position.x, v->position.y, v->position.z); glutSolidSphere(h, 10, 10); glPopMatrix(); } } glEnd(); glPopAttrib(); } void Viewer :: drawVectorField( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); double h = 0.25*mesh.meanEdgeLength(); glDisable( GL_LIGHTING ); glColor3f( 0., 0., 0. ); glLineWidth( 2.0 ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { if( f->isBoundary() ) continue; Vector a = f->barycenter(); Vector b = a + h*f->vector; Vector c = a - h*f->vector; Vector n = f->normal(); Vector v = b - a; Vector v90 = cross(n, v); Vector p0 = b; Vector p1 = p0 - 0.2 * v - 0.1 * v90; Vector p2 = p0 - 0.2 * v + 0.1 * v90; glBegin( GL_LINES ); glVertex3dv( &c[0] ); glVertex3dv( &b[0] ); glEnd(); glBegin(GL_TRIANGLES); glVertex3dv( &p0[0] ); glVertex3dv( &p1[0] ); glVertex3dv( &p2[0] ); glEnd(); } glPopAttrib(); } void Viewer :: pickVertex(int x, int y) { int width = glutGet(GLUT_WINDOW_WIDTH ); int height = glutGet(GLUT_WINDOW_HEIGHT); if( x < 0 || x >= width || y < 0 || y >= height ) return; int bufSize = mesh.vertices.size(); GLuint* buf = new GLuint[bufSize]; glSelectBuffer(bufSize, buf); GLint viewport[4]; GLdouble projection[16]; glGetIntegerv( GL_VIEWPORT, viewport ); glGetDoublev(GL_PROJECTION_MATRIX, projection); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix(x, viewport[3]-y, 10, 10, viewport); glMultMatrixd(projection); glMatrixMode(GL_MODELVIEW); glPushMatrix(); drawVertices(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); long hits = glRenderMode(GL_RENDER); int index = -1; double min_z = 1.0e100; for( long i = 0; i < hits; ++i ) { double distance = buf[4*i + 1]; if( distance < min_z ) { index = buf[4*i + 3]; min_z = distance; } } delete[] buf; if (index >= 0) { if( mesh.vertices[index].tag ) return; if( indices.size() >= age.size() ) { std::cout << "Reached max number of picked vertices" << std::endl; return; } mesh.vertices[index].toggleTag(); indices.push_back(index); mIncreaseIndexV( indices.size()-1 ); updateDisplayList(); } } void Viewer :: drawGenerators( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_COLOR_MATERIAL ); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glLineWidth(3.0); for(unsigned i = 0; i < mesh.generators.size(); ++i) { if( mesh.isBoundaryGenerator( mesh.generators[i] ) ) glColor3d(0.0,0.0,0.5); else glColor3d(0.0,0.5,0.0); for(unsigned j = 0; j < mesh.generators[i].size(); ++j) { HalfEdgeIter h = mesh.generators[i][j]; if( h->onBoundary or h->flip->onBoundary ) continue; Vector p0 = h->vertex->position; Vector p1 = h->flip->vertex->position; Vector m = 0.5*(p0 + p1); Vector f0 = h->face->barycenter(); Vector f1 = h->flip->face->barycenter(); Vector n0 = h->face->normal(); Vector n1 = h->flip->face->normal(); glBegin(GL_LINES); glNormal3dv( &n0[0] ); glVertex3dv( &f0[0] ); glVertex3dv( & m[0] ); glNormal3dv( &n1[0] ); glVertex3dv( & m[0] ); glVertex3dv( &f1[0] ); glEnd(); } } glPopAttrib(); } } ================================================ FILE: Connection/src/main.cpp ================================================ #include using namespace std; #include "Viewer.h" #include "Direction.h" using namespace DDG; int main( int argc, char** argv ) { if( argc != 2 ) { cerr << "usage: " << argv[0] << " in.obj" << endl; return 1; } Viewer viewer; viewer.mesh.read( argv[1] ); viewer.mesh.init(); DirectionField field; field.generate( viewer.mesh, viewer.angle ); viewer.init(); return 0; } ================================================ FILE: Elasticity/.viewer_state.txt ================================================ 0.126663 -0.0243652 -0.989174 0.0699843 815 767 ================================================ FILE: Elasticity/Makefile ================================================ ########################################################################################## # Specify library locations here (add or remove "#" marks to comment/uncomment lines for your platform) # Mac OS X DDG_INCLUDE_PATH = DDG_LIBRARY_PATH = DDG_BLAS_LIBS = -framework Accelerate DDG_SUITESPARSE_LIBS = -lspqr -lumfpack -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -ltbb -lm -lsuitesparseconfig DDG_OPENGL_LIBS = -framework OpenGL -framework GLUT # # Linux # DDG_INCLUDE_PATH = # DDG_LIBRARY_PATH = # DDG_BLAS_LIBS = -llapack -lblas -lgfortran # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut -lGL -lGLU -lX11 # # Windows / Cygwin # DDG_INCLUDE_PATH = -I/usr/include/opengl -I/usr/include/suitesparse # DDG_LIBRARY_PATH = -L/usr/lib/w32api -L/usr/lib/suitesparse # DDG_BLAS_LIBS = -llapack -lblas # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut32 -lglu32 -lopengl32 ######################################################################################## TARGET = elasticity CC = g++ LD = g++ CFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_INCLUDE_PATH) -I./include -I./src LFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_LIBRARY_PATH) LIBS = $(DDG_OPENGL_LIBS) $(DDG_SUITESPARSE_LIBS) $(DDG_BLAS_LIBS) ######################################################################################## ## !! Do not edit below this line HEADERS := $(wildcard include/*.h) SOURCES := $(wildcard src/*.cpp) OBJECTS := $(addprefix obj/,$(notdir $(SOURCES:.cpp=.o))) all: $(TARGET) $(TARGET): $(OBJECTS) $(LD) $(OBJECTS) -o $(TARGET) $(CFLAGS) $(LFLAGS) $(LIBS) obj/%.o: src/%.cpp ${HEADERS} $(CC) -c $< -o $@ $(CFLAGS) clean: rm -f $(OBJECTS) rm -f $(TARGET) rm -f $(TARGET).exe ================================================ FILE: Elasticity/include/Application.h ================================================ /* * A Simple Geometric Model for Elastic Deformations * Isaac Chao, Ulrich Pinkall, Patrick Sanan, Peter Schröder * ACM Transactions on Graphics, 29(4), 2010, 38:1-38:6. * * TODO: implemented just a gradient descent; use full Newton's method * */ #ifndef DDG_APPLICATION_H #define DDG_APPLICATION_H #include "Mesh.h" #include "Complex.h" #include "DenseMatrix.h" #include "SparseMatrix.h" #include "DiscreteExteriorCalculus.h" #include "PolarDecomposition2x2.h" namespace DDG { class Application { public: void interpolate(const double t, const Mesh& source, const Mesh& target, Mesh& mesh, int max_iters = 20, double tolerance = 1.0e-8) { SparseMatrix src_L; computeLaplacian(source, src_L); SparseMatrix tgt_L; computeLaplacian(target, tgt_L); SparseMatrix L = Complex(1.-t)*src_L + Complex(t)*tgt_L; SparseFactor LL; LL.build( L ); for( int iter = 0; iter < max_iters; iter++ ) { // extract rotation from deformation gradient DenseMatrix src_angle; computeRotation(source, mesh, src_angle); DenseMatrix tgt_angle; computeRotation(target, mesh, tgt_angle); // optimize vertices' position DenseMatrix src_rhs; computeDivergence(source, src_angle, src_rhs); DenseMatrix tgt_rhs; computeDivergence(target, tgt_angle, tgt_rhs); DenseMatrix rhs = Complex(1.-t)*src_rhs + Complex(t)*tgt_rhs; DenseMatrix x; get2DPositions(mesh, x); double res = residual(L, x, rhs); std::cout << "Iter" << iter << ": res = " << res << std::endl; if (res < tolerance) break; backsolvePositiveDefinite(LL, x, rhs); x.removeMean(); assign2DPositions(x, mesh); } } void init(const double step, const Mesh& source, const Mesh& target, Mesh& mesh) { VertexCIter sv = source.vertices.begin(); VertexCIter tv = target.vertices.begin(); VertexIter v = mesh.vertices.begin(); for( ; v != mesh.vertices.end(); v++, tv++, sv++) { v->position = (1.-step)*sv->position + step*tv->position; } } protected: void computeRotation(const Mesh& meshA, const Mesh& meshB, DenseMatrix& angle) const { angle = DenseMatrix(meshA.faces.size(),1); FaceCIter fA = meshA.faces.begin(); FaceCIter fB = meshB.faces.begin(); for( ; fA != meshA.faces.end(); fA++, fB++) { // edge vectors from A VertexIter v0 = fA->he->vertex; VertexIter v1 = fA->he->next->vertex; VertexIter v2 = fA->he->next->next->vertex; Vector e01 = v1->position - v0->position; Vector e12 = v2->position - v1->position; DenseMatrix A0(2,2); A0(0,0) = e01.x; A0(0,1) = e12.x; A0(1,0) = e01.y; A0(1,1) = e12.y; // edge vectors from B v0 = fB->he->vertex; v1 = fB->he->next->vertex; v2 = fB->he->next->next->vertex; e01 = v1->position - v0->position; e12 = v2->position - v1->position; DenseMatrix A1(2,2); A1(0,0) = e01.x; A1(0,1) = e12.x; A1(1,0) = e01.y; A1(1,1) = e12.y; // ortho(A1*inv(A0)) DenseMatrix M = A1 * PolarDecomposition2x2::invert(A0); Complex z = PolarDecomposition2x2::extractOrthogonalPart(M); angle(fA->index,0) = z; } } void computeLaplacian(const Mesh& mesh, SparseMatrix& L) const { SparseMatrix star0; SparseMatrix star1; HodgeStar0Form::build( mesh, star0 ); HodgeStar1Form::build( mesh, star1 ); SparseMatrix d0; ExteriorDerivative0Form::build( mesh, d0 ); L = d0.transpose() * star1 * d0; L += Complex(1e-8)*star0; } void computeDivergence(const Mesh& mesh, const DenseMatrix& angle, DenseMatrix& rhs) const { rhs = DenseMatrix(mesh.vertices.size(),1); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { Complex sum; Vector pi = v->position; HalfEdgeIter he = v->he; do { if( not he->onBoundary ) { Vector area_grad = 0.5*he->next->rotatedEdge(); Complex sum_ijk( area_grad.x, area_grad.y ); Complex Rijk = angle(he->face->index, 0); sum += Rijk * sum_ijk; } he = he->flip->next; } while( he != v->he ); rhs(v->index,0) = sum; } } void assign2DPositions(const DenseMatrix& x, Mesh& mesh) { for( VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { Complex xi = x(v->index,0); v->position = Vector(xi.re, xi.im, 0.); } } void get2DPositions(const Mesh& mesh, DenseMatrix& x) const { x = DenseMatrix(mesh.vertices.size(),1); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { Complex z(v->position.x, v->position.y); x(v->index,0) = z; } } }; } #endif ================================================ FILE: Elasticity/include/Camera.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Camera.h // ----------------------------------------------------------------------------- // // Camera is used by Viewer to keep track of the view state; it also // handles mouse input related to camera manipulation. // #ifndef DDG_CAMERA_H #define DDG_CAMERA_H #include "Quaternion.h" #ifdef __CYGWIN__ #define GLUT_DISABLE_ATEXIT_HACK #include #include #include #include #else #include #endif namespace DDG { class Camera { public: Camera( void ); // constructor Quaternion clickToSphere( int x, int y ); // projects a mous click onto the unit sphere void setView( void ) const; // applies the camera transformation to the OpenGL modelview stack void mouse( int button, int state, int x, int y ); // handles mouse clicks void motion( int x, int y ); // handles mouse drags void idle( void ); // handles camera momentum void zoomIn( void ); // moves viewer toward object void zoomOut( void ); // moves viewer away from object Quaternion currentRotation( void ) const; // returns the rotation corresponding to the current mouse state Quaternion pClick; // mouse coordinates of current click Quaternion pDrag; // mouse coordinates of current drag Quaternion pLast; // mouse coordinates of previous drag Quaternion rLast; // previous camera rotation Quaternion momentum; // camera momentum int tLast; // time of previous drag double zoom, vZoom; // zoom and zoom velocity }; } #endif ================================================ FILE: Elasticity/include/Complex.h ================================================ #ifndef DDG_COMPLEX_H #define DDG_COMPLEX_H #include namespace DDG { class Complex { public: Complex( double a=0., double b=0. ); // constructs number a+bi void operator+=( const Complex& z ); // add z void operator-=( const Complex& z ); // subtract z void operator*=( const Complex& z ); // Complex multiply by z void operator*=( double r ); // scalar multiply by r void operator/=( double r ); // scalar divide by r void operator/=( const Complex& z ); // complex divide by r Complex operator-( void ) const; // returns the additive inverse Complex conj( void ) const; // returns Complex conjugate Complex inv( void ) const; // returns inverse double arg( void ) const; // returns argument double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Complex unit( void ) const; // returns complex number with unit norm and same modulus Complex exponential( void ) const; // complex exponentiation double re; // real part double im; // imaginary part }; Complex operator+( const Complex& z1, const Complex& z2 ); // binary addition Complex operator-( const Complex& z1, const Complex& z2 ); // binary subtraction Complex operator*( const Complex& z1, const Complex& z2 ); // binary Complex multiplication Complex operator*( const Complex& z, double r ); // right scalar multiplication Complex operator*( double r, const Complex& z ); // left scalar multiplication Complex operator/( const Complex& z, double r ); // scalar division Complex operator/( const Complex& z1, const Complex& z2 ); // complex division double dot( const Complex& z1, const Complex& z2 ); // inner product double cross( const Complex& z1, const Complex& z2 ); // cross product std::ostream& operator<<( std::ostream& os, const Complex& o ); // prints components } #endif ================================================ FILE: Elasticity/include/DenseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DenseMatrix.h // ----------------------------------------------------------------------------- // // DenseMatrix represents an m by n (real or complex) matrix where every // entry -- including zero-valued entries -- is stored explicitly. This // class is most commonly used to represent dense vectors in sparse linear // systems (i.e., the right hand side and the solution vector). // // A real or complex matrix is allocated via // // DenseMatrix A( m, n ); // DenseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // DenseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a DenseMatrix returns a cholmod_dense* // which can be used by routines in SuiteSparse. For basic operations, however, // you should not need to access this pointer explicitly -- see the solve() // method in SparseMatrix.h. // #ifndef DDG_DENSEMATRIX_H #define DDG_DENSEMATRIX_H #include #include "Types.h" #include namespace DDG { enum NormType { lInfinity, lOne, lTwo }; template class DenseMatrix { public: DenseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix (specifying just m yields a column vector) DenseMatrix( const DenseMatrix& A ); // copy constructor const DenseMatrix& operator=( const DenseMatrix& B ); // copies B ~DenseMatrix( void ); // destructor SparseMatrix sparse( void ); // converts to a sparse matrix int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val = 0. ); // sets all elements to val double norm( NormType type = lInfinity ) const; // returns the maximum magnitude of any entry T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element of the matrix (uses 0-based indexing) T& operator()( int index ); T operator()( int index ) const; // access the specified element of a vector (uses 0-based indexing) DenseMatrix transpose( void ) const; // returns the transpose of this matrix DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c DenseMatrix operator+( const DenseMatrix& B ) const; // returns sum of this matrix with B void operator+=( const DenseMatrix& B ); // adds B to this matrix DenseMatrix operator-( const DenseMatrix& B ) const; // returns difference of this matrix with B void operator-=( const DenseMatrix& B ); // subtracts B from this matrix DenseMatrix operator-( void ) const; // returns additive inverse of this matrix cholmod_dense* to_cholmod( void ); // returns pointer to copy of matrix in CHOLMOD format const DenseMatrix& operator=( cholmod_dense* B ); // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B void normalize( void ); // divides by Frobenius norm T sum( void ) const; // returns the sum of all entries void removeMean( void ); // removes the mean void randomize( void ); // replaces entries with uniformly distributed random real numbers in the interval [-1,1] protected: int m, n; std::vector data; cholmod_dense* cData; }; template DenseMatrix operator*( const DenseMatrix& A, const T& c ); // right scalar multiplication template DenseMatrix operator*( const T& c, const DenseMatrix& A ); // left scalar multiplication template DenseMatrix operator/( const DenseMatrix& A, const T& c ); // scalar division template T dot( const DenseMatrix& x, const DenseMatrix& y ); // returns Euclidean inner product of x and y template std::ostream& operator << (std::ostream& os, const DenseMatrix& o); // prints entries template T inner( const DenseMatrix& x, const DenseMatrix& y ); // standard inner product template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ); // inner product with respect to a diagonal inner // product B represented as a dense vector } #include "DenseMatrix.inl" #endif ================================================ FILE: Elasticity/include/DiscreteExteriorCalculus.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DiscreteExteriorCalculus.h // ----------------------------------------------------------------------------- // // Static methods for building the fundamental discrete operators (exterior // derivative, Hodge star) for 0-, 1-, and 2-forms on a surface mesh. Methods // are templated on entry type, i.e., one can build either real- or complex- // matrices using the types DDG::Real and DDG::Complex, respectively. For // instance, to build the usual Laplacian on functions, one could write // // Mesh mesh; // SparseMatrix d0, star0, star1, Delta; // // ExteriorDerivative0Form::build( mesh, d0 ); // HodgeStar0Form::build( mesh, star0 ); // HodgeStar1Form::build( mesh, star1 ); // Delta = star0.inverse() * d0.transpose() * star1 * d0; // #ifndef DDG_DISCRETEEXTERIORCALCULUS_H #define DDG_DISCRETEEXTERIORCALCULUS_H #include "Mesh.h" #include "SparseMatrix.h" namespace DDG { template< class T > struct HodgeStar0Form { static void build( const Mesh& mesh, SparseMatrix& star0 ); }; template< class T > struct HodgeStar1Form { static void build( const Mesh& mesh, SparseMatrix& star1 ); }; template< class T > struct HodgeStar2Form { static void build( const Mesh& mesh, SparseMatrix& star2 ); }; template< class T > struct ExteriorDerivative0Form { static void build( const Mesh& mesh, SparseMatrix& d0 ); }; template< class T > struct ExteriorDerivative1Form { static void build( const Mesh& mesh, SparseMatrix& d1 ); }; } #include "DiscreteExteriorCalculus.inl" #endif ================================================ FILE: Elasticity/include/Edge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Edge.h // ----------------------------------------------------------------------------- // // Edge stores attributes associated with a mesh edge. The iterator he points // to one of its two associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_EDGE_H #define DDG_EDGE_H #include "Types.h" namespace DDG { class Edge { public: HalfEdgeIter he; // points to one of the two halfedges associated with this edge int index; // unique integer ID in the range 0, ..., nEdges-1 Edge() : index(0) { } }; } #endif ================================================ FILE: Elasticity/include/Face.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Face.h // ----------------------------------------------------------------------------- // // Face stores attributes associated with a mesh edge. The iterator he points // to one of its associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_FACE_H #define DDG_FACE_H #include "Types.h" namespace DDG { class Face { public: HalfEdgeIter he; // points to one of the halfedges associated with this face int index; // unique integer ID in the range 0, ..., nFaces-1 Face() : index(0) { } bool isBoundary( void ) const; // returns true if this face corresponds to a // boundary loop; false otherwise double area( void ) const; // returns the triangle area Vector normal( void ) const; // returns the unit normal associated with this face; normal // orientation is determined by the circulation order of halfedges Vector circumcenter( void ) const; // returns triangle circumcenter Vector barycenter( void ) const; // returns triangle barycenter }; } #endif ================================================ FILE: Elasticity/include/HalfEdge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- HalfEdge.h // ----------------------------------------------------------------------------- // // HalfEdge is used to define mesh connectivity. (See the documentation for a // more in-depth discussion of the halfedge data structure.) // #ifndef DDG_HALFEDGE_H #define DDG_HALFEDGE_H #include "Vector.h" #include "Types.h" namespace DDG { class HalfEdge { public: HalfEdgeIter next; // points to the next halfedge around the current face HalfEdgeIter flip; // points to the other halfedge associated with this edge VertexIter vertex; // points to the vertex at the "tail" of this halfedge EdgeIter edge; // points to the edge associated with this halfedge FaceIter face; // points to the face containing this halfedge bool onBoundary; // true if this halfedge is contained in a boundary // loop; false otherwise Vector texcoord; // texture coordinates associated with the triangle corner at the // "tail" of this halfedge double cotan( void ) const; // returns the cotangent of the angle opposing this edge Vector rotatedEdge( void ) const; // returns oriented edge vector rotated by PI/2 around face normal // if onBoundary, then return nil }; } #endif ================================================ FILE: Elasticity/include/Image.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Image.h // ----------------------------------------------------------------------------- // // Image represents a color bitmap image. A simple example might look like // // Image im; // im.read( "input.tga" ); // // modify image data via im(x,y) = ...; // im.write( "output.tga" ); // #ifndef DDG_IMAGE_H #define DDG_IMAGE_H #include #include namespace DDG { class Image { public: Image( int width = 0, int height = 0 ); // constructs image with specified width and height float& operator()( int x, int y ); const float& operator()( int x, int y ) const; // accesses pixel (x,y) float sample( float x, float y ) const; // samples image at (x,y) using bilinear filtering int width( void ) const; int height( void ) const; // returns image dimensions void read( const char* filename ); // loads an image file in Truevision TGA format // (must be RGB image with 24 or 32 bits per pixel) void write( const char* filename ) const; // writes an image file in Truevision TGA format // (RGB image with 24 bits per pixel) protected: void clamp( int& x, int& y ) const; // clamps coordinates to range [0,w-1] x [0,h-1] int w, h; // width and height std::vector pixels; // interleaved RGBA pixel data in range [0-1] }; } #endif ================================================ FILE: Elasticity/include/LinearContext.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearContext.h // ----------------------------------------------------------------------------- // // LinearContext is the global solver context needed to interface with the // SuiteSparse library. It is essentially a wrapper around cholmod_common. A // single static instance of LinearContext is declared in LinearContext.cpp and // is shared by all instances of DenseMatrix, SparseMatrix, and LinearSystem. // In other words, you shouldn't have to instantiate LinearContext yourself // unless you're doing something really fancy! // #ifndef DDG_LINEARSOLVERCONTEXT #define DDG_LINEARSOLVERCONTEXT #include namespace DDG { class LinearContext { public: LinearContext( void ); // constructor ~LinearContext( void ); // destructor operator cholmod_common*( void ); // allows LinearContext to be treated as a cholmod_common* protected: cholmod_common context; }; } #endif ================================================ FILE: Elasticity/include/LinearEquation.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearEquation.h // ----------------------------------------------------------------------------- // // LinearEquation represents an equation with an arbitrary linear polynomial on // both the left- and right-hand side. It is primarily used while building a // LinearSystem. For convenience, operator== is overloaded so that the user // can construct a LinearEquation by writing something that looks much like the // usual mathematical syntax for a linear equation. For example, // // LinearEquation eqn = ( x + 2*y == 3*z ); // // builds the linear equation x + 2y = 3z. // #ifndef DDG_LINEAREQUATION_H #define DDG_LINEAREQUATION_H #include "LinearPolynomial.h" namespace DDG { class LinearEquation { public: LinearPolynomial lhs; // left-hand side LinearPolynomial rhs; // right-hand side }; LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ); // constructs a linear equation with the specified left- and right-hand side } #endif ================================================ FILE: Elasticity/include/LinearPolynomial.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearPolynomial.h // ----------------------------------------------------------------------------- // // LinearPolynomial represents an affine function of the form // // f(x1,x2,...,xn) = c1 x1 + c2 x2 + ... + cn xn + d // // where the xi are real-valued variables with real coefficients ci, and d is // a real constant. The variables and their coefficients are represented using // instances of the Variable class. LinearPolynomial implements all the usual // algebraic operations on affine functions, as well as type conversions from // more elementary types (scalars, single variables, etc.). // // Importantly, variables used in a LinearPolynomial should *not* be deallocated // while the polynomial is still in use -- LinearPolynomial stores only a // reference to these variables so that the solution to a linear system can be // automatically copied back into the variables. // #ifndef DDG_LINEARPOLYNOMIAL_H #define DDG_LINEARPOLYNOMIAL_H #include #include #include "Variable.h" namespace DDG { class LinearPolynomial { public: LinearPolynomial( void ); // constructs the zero function LinearPolynomial( double c ); // constructs the constant function with value c LinearPolynomial( Variable& v ); // constructs a function with a single variable v const LinearPolynomial& operator=( double c ); // assigns the constant function with value c const LinearPolynomial& operator=( Variable& v ); // assigns a function with a single variable v void operator+=( double c ); void operator-=( double c ); void operator*=( double c ); void operator/=( double c ); // adds, subtract, multiplies, or divides by a constant void operator+=( Variable& v ); void operator-=( Variable& v ); // increments or decrements by a single variable v void operator+=( const LinearPolynomial& p ); void operator-=( const LinearPolynomial& p ); // increments or decrements by an affine function LinearPolynomial operator-( void ) const; // returns the additive inverse (i.e., negation) double evaluate( void ) const; // evaluates the function using the current values of its variables std::map linearTerms; // list of linear terms double constantTerm; // constant term }; LinearPolynomial operator+( double c, Variable& v ); // sum LinearPolynomial operator+( Variable& v, double c ); // sum LinearPolynomial operator-( double c, Variable& v ); // difference LinearPolynomial operator-( Variable& v, double c ); // difference LinearPolynomial operator*( double c, Variable& v ); // product LinearPolynomial operator*( Variable& v, double c ); // product LinearPolynomial operator/( Variable& v, double c ); // quotient // algebraic operations between single variables and constants LinearPolynomial operator+( Variable& v1, Variable& v2 ); // sum LinearPolynomial operator-( Variable& v1, Variable& v2 ); // difference // algebraic operations between pairs of variables LinearPolynomial operator+( const LinearPolynomial& p, double c ); // sum LinearPolynomial operator+( double c, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, double c ); // difference LinearPolynomial operator-( double c, const LinearPolynomial& p ); // difference LinearPolynomial operator*( const LinearPolynomial& p, double c ); // product LinearPolynomial operator*( double c, const LinearPolynomial& p ); // product LinearPolynomial operator/( const LinearPolynomial& p, double c ); // quotient // algebraic operations between polynomials and constants LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ); // sum LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ); // difference LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ); // difference // algebraic operations between polynomials and single variables LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ); // sum LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ); // difference // algebraic operations between pairs of polynomials std::ostream& operator<<( std::ostream& os, const LinearPolynomial& p ); // prints the symbolic representation of a polynomial (all variables must be named) } #endif ================================================ FILE: Elasticity/include/LinearSystem.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearSystem.h // ----------------------------------------------------------------------------- // // LinearSystem represents a system of linear equations expressed in terms of // instances of the Variable class. The main idea is to make it easy to // construct and solve linear systems without explicitly think about variable // indices, matrix layout, etc. (This kind of abstraction is particularly // useful for debugging and rapid prototyping.) See the documentation for // examples of building linear and solving systems. // // Importantly, any variable used by a LinearSystem should not be deallocated // while the system is still in use, because the method LinearSystem::solve() // automatically copies the solution back into the variables used to define the // equations. (In the future variables may become reference-counted in order // to avoid this issue.) // // Note that LinearSystem::solve() uses a general-purpose linear solver (namely // the sparse QR factorization found in SuiteSparse) that is quite fast but may // not always be your best option. To improve performance you may want to // build the system explicitly using an instance of SparseMatrix and call a // more specialized solver. (In the future there may be options for specifying // that a LinearSystem is, e.g., symmetric and positive-definite.) // #ifndef DDG_LINEARSYSTEM_H #define DDG_LINEARSYSTEM_H #include #include "LinearEquation.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "Real.h" namespace DDG { class LinearSystem { public: void clear( void ); // removes all equations from the system void push_back( const LinearEquation& e ); // appends the equation e to the sytem void solve( void ); // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution std::vector equations; // the collection of equations defining the system protected: void convertEquations( void ); void indexVariables( void ); void buildSparseMatrix( void ); void buildRightHandSide( void ); void computeSolution( void ); int nEquations; int nVariables; std::vector currentEquations; std::map index; SparseMatrix A; DenseMatrix x; DenseMatrix b; }; } #endif ================================================ FILE: Elasticity/include/Mesh.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Mesh.h // ----------------------------------------------------------------------------- // // Mesh represents a polygonal surface mesh using the halfedge data structure. // It is essentially a large collection of disjoint vertices, edges, and faces // that are ``glued together'' by halfedges which encode connectivity (see // the documentation for an illustration). By construction, the halfedge data // structure cannot represent nonorientable surfaces or meshes with nonmanifold // edges. // // Mesh elements are referenced using iterators -- common usage of these // iterators is to either traverse an entire vector of mesh elements: // // // visit all vertices // for( VertexIter i = vertices.begin(); i != vertices.end(); i++ ) // { // //... // } // // or to perform a local traversal over the neighborhood of some mesh element: // // // visit both halfedges of edge e // HalfEdgeIter he = e->he; // do // { // // ... // // he = he->flip; // } // while( he != e->he ); // // (See Types.h for an explicit definition of iterator types.) // // Meshes with boundary are handled by creating an additional face for each // boundary loop (the method Face::isBoundary() determines whether a given // face is a boundary loop). Isolated vertices (i.e., vertiecs not contained // in any edge or face) reference a dummy halfedge and can be checked via // the method Vertex::isIsolated(). // #ifndef DDG_MESH_H #define DDG_MESH_H #include #include #include "HalfEdge.h" #include "Vertex.h" #include "Edge.h" #include "Face.h" #include "SparseMatrix.h" namespace DDG { class Mesh { public: Mesh( void ); // constructs an empty mesh Mesh( const Mesh& mesh ); // constructs a copy of mesh const Mesh& operator=( const Mesh& mesh ); // copies mesh int read( const std::string& filename ); // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error int write( const std::string& filename ) const; // writes a mesh to a Wavefront OBJ file; return value is nonzero // only if there was an error bool reload( void ); // reloads a mesh from disk using the most recent input filename void normalize( void ); // centers around the origin and rescales to have unit radius double area( void ) const; // returns total mesh area double meanEdgeLength( void ) const; // returns mean edge lenght std::vector halfedges; std::vector vertices; std::vector edges; std::vector faces; std::vector boundaries; // storage for mesh elements protected: std::string inputFilename; void indexElements( void ); // assigns a unique, 0-based index to each mesh element }; } #endif ================================================ FILE: Elasticity/include/MeshIO.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- MeshIO.h // ----------------------------------------------------------------------------- // // MeshIO handles input/output operations for Mesh objects. Currently the only // supported mesh format is Wavefront OBJ -- for a format specification see // // http://en.wikipedia.org/wiki/Wavefront_.obj_file // // Note that vertex normals and material properties are currently ignored. // #ifndef DDG_MESHIO_H #define DDG_MESHIO_H #include #include #include #include namespace DDG { class Mesh; class Index; class MeshData; class MeshIO { public: static int read( std::istream& in, Mesh& mesh ); // reads a mesh from a valid, open input stream in static void write( std::ostream& out, const Mesh& mesh ); // writes a mesh to a valid, open output stream out protected: static int readMeshData( std::istream& in, MeshData& data ); static void readPosition( std::stringstream& ss, MeshData& data ); static void readTexCoord( std::stringstream& ss, MeshData& data ); static void readNormal ( std::stringstream& ss, MeshData& data ); static void readFace ( std::stringstream& ss, MeshData& data ); static Index parseFaceIndex( const std::string& token ); static void preallocateMeshElements( const MeshData& data, Mesh& mesh ); static int buildMesh( const MeshData& data, Mesh& mesh ); static void checkIsolatedVertices( const Mesh& Mesh ); static void checkNonManifoldVertices( const Mesh& Mesh ); }; } #endif ================================================ FILE: Elasticity/include/PolarDecomposition2x2.h ================================================ #ifndef POLAR_DECOMPOSITION_2x2_H #define POLAR_DECOMPOSITION_2x2_H #include "Complex.h" #include "DenseMatrix.h" namespace DDG { class PolarDecomposition2x2 { public: static Complex extractOrthogonalPart(DenseMatrix A) { double res = 1.0; while( res > 1.0e-6 ) { DenseMatrix B = Real(0.5)*( A + invert(A.transpose()) ); res = (A - B).norm( lTwo ); A = B; } return Complex( A(0,0), A(1,0) ).unit(); } static DenseMatrix invert(const DenseMatrix& A) { double detA = A(0,0)*A(1,1) - A(0,1)*A(1,0); DenseMatrix B(2,2); B(0,0) = A(1,1)/detA; B(0,1) = -A(0,1)/detA; B(1,0) = -A(1,0)/detA; B(1,1) = A(0,0)/detA; return B; } }; } #endif ================================================ FILE: Elasticity/include/Quaternion.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Quaternion.h // ----------------------------------------------------------------------------- // // Quaternion represents an element of the quaternions, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // Hamilton product is expressed using the * operator: // // Quaternion p, q, r; // r = q * p; // // and conjugation is expressed using the method Quaternion::conj(): // // Quaternion q; // double normQSquared = -q.conj()*q; // // Individual components can be accessed in several ways: the real and imaginary // parts can be accessed using the methods Quaternion::re() and Quaternion::im(): // // Quaternion q; // double a = q.re(); // Vector b = q.im(); // // or by index: // // Quaternion q; // double a = q[0]; // double bi = q[1]; // double bj = q[2]; // double bk = q[3]; // #ifndef DDG_QUATERNION_H #define DDG_QUATERNION_H #include "Vector.h" #include "Complex.h" #include namespace DDG { class Quaternion { public: Quaternion( void ); // initializes all components to zero Quaternion( const Quaternion& q ); // initializes from existing quaternion Quaternion( double s, double vi, double vj, double vk ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s, const Vector& v ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s ); // initializes purely real quaternion with specified real (s) component (imaginary part is zero) Quaternion( const Vector& v ); // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) Quaternion( const Complex& z ); // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k const Quaternion& operator=( double s ); // assigns a purely real quaternion with real value s const Quaternion& operator=( const Vector& v ); // assigns a purely real quaternion with imaginary value v double& operator[]( int index ); // returns reference to the specified component (0-based indexing: r, i, j, k) const double& operator[]( int index ) const; // returns const reference to the specified component (0-based indexing: r, i, j, k) void toMatrix( double Q[4][4] ) const; // builds 4x4 matrix Q representing (left) quaternion multiplication double& re( void ); // returns reference to double part const double& re( void ) const; // returns const reference to double part Vector& im( void ); // returns reference to imaginary part const Vector& im( void ) const; // returns const reference to imaginary part Quaternion operator+( const Quaternion& q ) const; // addition Quaternion operator-( const Quaternion& q ) const; // subtraction Quaternion operator-( void ) const; // negation Quaternion operator*( double c ) const; // right scalar multiplication Quaternion operator/( double c ) const; // scalar division void operator+=( const Quaternion& q ); // addition / assignment void operator+=( double c ); // addition / assignment of pure real void operator-=( const Quaternion& q ); // subtraction / assignment void operator-=( double c ); // subtraction / assignment of pure real void operator*=( double c ); // scalar multiplication / assignment void operator/=( double c ); // scalar division / assignment Quaternion operator*( const Quaternion& q ) const; // Hamilton product void operator*=( const Quaternion& q ); // Hamilton product / assignment Quaternion conj( void ) const; // conjugation Quaternion inv( void ) const; // inverse double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Quaternion unit( void ) const; // returns unit quaternion void normalize( void ); // divides by Euclidean length protected: double s; // scalar (double) part Vector v; // vector (imaginary) part }; Quaternion operator*( double c, const Quaternion& q ); // left scalar multiplication std::ostream& operator<<( std::ostream& os, const Quaternion& q ); // prints components } #endif ================================================ FILE: Elasticity/include/Real.h ================================================ #ifndef DDG_REAL_H #define DDG_REAL_H namespace DDG { class Real { public: Real( double x = 0. ); // constructs real number with value x operator double( void ) const; // type cast to double void operator+=( double x ); // increment void operator-=( double x ); // decrement void operator*=( double x ); // multiply void operator/=( double x ); // divide Real conj( void ) const; // simply returns the value (for compatibility w/ complex numbers) Real inv( void ) const; // returns inverse double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Real unit( void ) const; // returns number with unit norm and same sign protected: double value; // value }; } #endif ================================================ FILE: Elasticity/include/Shader.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Shader.h // ----------------------------------------------------------------------------- // // Shader encapsulates the functionality of a shader program written in // the OpenGL Shader Language (GLSL). Basic usage is to read a collection // of source files to disk and enable the shader before making draw calls. // For instance, during initialization one might write // // Shader shader; // shader.loadVertex( "vertex.glsl" ); // shader.loadFragment( "fragment.glsl" ); // // and in the main draw routine write // // shader.enable(); // // draw some stuff // shader.disable(); // #ifndef DDG_SHADER_H #define DDG_SHADER_H #include #include namespace DDG { class Shader { public: Shader( void ); // constructor -- shader is initially invalid ~Shader( void ); // destructor void loadVertex( const char* filename ); // read vertex shader from GLSL source file void loadFragment( const char* filename ); // read fragment shader from GLSL source file void loadGeometry( const char* filename ); // read geometry shader from GLSL source file void enable( void ); // uses this shader for rendering void disable( void ) const; // uses the fixed-function pipeline for rendering operator GLuint( void ) const; // returns the ID of this shader program (for calls to OpenGL) protected: void load( GLenum shaderType, const char* filename, GLuint& shader ); bool readSource( const char* filename, std::string& source ); GLuint vertexShader; GLuint fragmentShader; GLuint geometryShader; GLuint program; bool linked; }; } #endif ================================================ FILE: Elasticity/include/SparseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- SparseMatrix.h // ----------------------------------------------------------------------------- // // SparseMatrix represents an m by n (real or complex) matrix where only // nonzero entries are stored explicitly. This class is most commonly // used to represent the linear term in sparse linear systems (i.e., the matrix // part). // // A real or complex matrix is allocated via // // SparseMatrix A( m, n ); // SparseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // SparseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a SparseMatrix returns a // cholmod_sparse* which can be used by routines in SuiteSparse. For basic // operations, however, you should not need to access this pointer explicitly -- // see the solve() method below. // // Internally SparseMatrix stores nonzero entries in a heap data structure; the // amortized cost of insertion is therefore no worse than the sorting cost of // putting the matrix in compressed-column order. // #ifndef DDG_SPARSE_MATRIX_H #define DDG_SPARSE_MATRIX_H #include #include #include #include "Types.h" namespace DDG { template class SparseMatrix { public: SparseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix SparseMatrix( const SparseMatrix& B ); // copy constructor ~SparseMatrix( void ); // destructor const SparseMatrix& operator=( const SparseMatrix& B ); // copies B const SparseMatrix& operator=( cholmod_sparse* B ); // copies a cholmod_sparse* into a SparseMatrix; // takes responsibility for deallocating B void resize( int m, int n ); // clears and resizes to mxn matrix SparseMatrix transpose( void ) const; // returns the transpose of this matrix cholmod_sparse* to_cholmod( void ); // returns pointer to copy of matrix in compressed-column CHOLMOD format SparseMatrix operator*( const SparseMatrix& B ) const; // returns product of this matrix with sparse B DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with dense B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c void operator+=( const SparseMatrix& B ); // adds B to this matrix void operator-=( const SparseMatrix& B ); // subtracts B from this matrix SparseMatrix operator+( const SparseMatrix& B ) const; // returns sum of this matrix with B SparseMatrix operator-( const SparseMatrix& B ) const; // returns difference of this matrix with B int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val ); // sets all nonzero elements val SparseMatrix inverse( void ) const; // returns inverse -- for diagonal matrices only // (assertion occurs for non-diagonal matrices) static SparseMatrix identity( int N ); // returns the N x N identity matrix DenseMatrix full( void ) const; // converts to a dense matrix T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element (uses 0-based indexing) // TODO for legibility, replace w/ type where entries are named "row, // TODO col" instead of "first, second" (especially since we adopt the // TODO unorthodox convention of storing the column first) typedef std::pair EntryIndex; // convenience type for an entry index; note that we store column THEN // row, which makes it easier to build compressed column format typedef std::map EntryMap; typedef typename EntryMap::iterator iterator; typedef typename EntryMap::const_iterator const_iterator; // convenience types for storing and accessing entries iterator begin( void ); const_iterator begin( void ) const; iterator end( void ); const_iterator end( void ) const; // return iterators to first and last nonzero entries void shift( double c ); // adds c times the identity matrix to this matrix protected: int m, n; EntryMap data; cholmod_sparse* cData; void allocateSparse( void ); void setEntry( const_iterator e, int i, double* pr ); }; template SparseMatrix operator*( const SparseMatrix& A, const T& c ); // right scalar multiplication template SparseMatrix operator*( const T& c, const SparseMatrix& A ); // left scalar multiplication template SparseMatrix operator/( const SparseMatrix& A, const T& c ); // scalar division template std::ostream& operator << (std::ostream& os, const SparseMatrix& o); // prints entries template class SparseFactor { public: SparseFactor( void ); ~SparseFactor( void ); void build( SparseMatrix& A ); // factorizes positive-definite matrix A using CHOLMOD bool valid( void ) const; // returns true if the factor has been built; false otherwise cholmod_factor* to_cholmod( void ); // returns pointer to underlying cholmod_factor data structure protected: cholmod_factor *L; }; template void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse QR factorization template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse LU factorization template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ); // backsolves the prefactored positive definite sparse linear system LL'x = b template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ); // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ); // returns the max residual of the linear problem A x = b relative to the largest entry of the solution template double residual( const SparseMatrix& A, const DenseMatrix& x ); // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda B x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns /<(B-EE^T)x,x> } #include "SparseMatrix.inl" #endif ================================================ FILE: Elasticity/include/Types.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Types.h // ----------------------------------------------------------------------------- // // This file contains forward declarations of common types and definitions of // convenience types for standard iterators. // #ifndef DDG_TYPES_H #define DDG_TYPES_H #include #include #include namespace DDG { // forward declarations class Camera; class Complex; class Edge; class Face; class HalfEdge; class Image; class LinearContext; class LinearEquation; class LinearPolynomial; class LinearSystem; class Mesh; class MeshIO; class Quaternion; class Real; class Shader; class Variable; class Vector; class Vertex; class Viewer; template class DenseMatrix; template class SparseMatrix; // convenience types for iterators typedef std::map::iterator TermIter; typedef std::map::const_iterator TermCIter; typedef std::vector::iterator PolyIter; typedef std::vector::const_iterator PolyCIter; typedef std::vector::iterator EqnIter; typedef std::vector::const_iterator EqnCIter; typedef std::map::iterator IndexIter; typedef std::map::const_iterator IndexCIter; typedef std::vector::iterator HalfEdgeIter; typedef std::vector::const_iterator HalfEdgeCIter; typedef std::vector::iterator VertexIter; typedef std::vector::const_iterator VertexCIter; typedef std::vector::iterator EdgeIter; typedef std::vector::const_iterator EdgeCIter; typedef std::vector::iterator FaceIter; typedef std::vector::const_iterator FaceCIter; } #endif ================================================ FILE: Elasticity/include/Utility.h ================================================ #ifndef DDG_UTILITY_H #define DDG_UTILITY_H #include #include "Utility.h" #include "Complex.h" namespace DDG { inline double sqr( double x ) { return x*x; } inline double unitRand( void ) { const double rRandMax = 1. / (double) RAND_MAX; return rRandMax * (double) rand(); } inline double seconds( int t0, int t1 ) { return (double)(t1-t0) / (double) CLOCKS_PER_SEC; } } namespace DDGConstants { static DDG::Complex ii( 0., 1. ); } #endif ================================================ FILE: Elasticity/include/Variable.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Variable.h // ----------------------------------------------------------------------------- // // Variable represents a variable that can be used to define a (linear or // nonlinear) system of equations. Its main feature is that it can be used // both as an abstract variable (e.g., when used to define an equation) but can // also store a definite numerical value. For instance, suppose we define a // linear polynomial // // Variable x, y; // LinearPolynomial p = x + 2*y; // // Now by assigning different numerical values to x and y, we can evaluate the // polynomial at different points: // // *x = 1; // *y = 2; // cout << p.evaluate() << endl; // // *x = 3; // *y = 4; // cout << p.evaluate() << endl; // // In general the dereference operator (*) accesses the numerical value. // Variables can also be named in order to aid with debugging. For instance, // // Variable x( "x" ); // Variable y( "y" ); // Polynomial p = x + 2*y; // cout << p << endl; // // will print out something like "x+2*y". // // The "fixed" flag in a variable refers to whether it is held constant while // solving a system of equations -- see the documentation for further discussion. // #ifndef DDG_VARIABLE_H #define DDG_VARIABLE_H #include namespace DDG { class Variable { public: Variable( double value = 0., bool fixed = false ); // initialize a variable which has value zero and is not fixed by default Variable( std::string name, double value = 0., bool fixed = false ); // initialize a named variable which has value zero and is not fixed by default double& operator*( void ); // returns a reference to the numerical value const double& operator*( void ) const; // returns a const reference to the numerical value std::string name; // names the variable (for display output) double value; // numerical value bool fixed; // true if a variable is held constant while solving a system of equations }; } #endif ================================================ FILE: Elasticity/include/Vector.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vector.h // ----------------------------------------------------------------------------- // // Vector represents an element of Euclidean 3-space, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // inner product (i.e., scalar or dot product) is expressed using the global // method dot(): // // Vector u, v; // double cosTheta = dot( u, v ); // // and the cross product is expressed using the global method cross(): // // Vector u, v, w; // w = cross( u, v ); // // Individual components can be accessed in two ways: either directly via the // members x, y, and z: // // Vector v; // cout << v.x << endl; // cout << v.y << endl; // cout << v.z << endl; // // or by index: // // Vector v; // for( int i = 0; i < 3; i++ ) // { // cout << v[i] << endl; // } // #ifndef DDG_VECTOR_H #define DDG_VECTOR_H #include namespace DDG { class Vector { public: Vector(); // initializes all components to zero Vector( double x, double y, double z); // initializes with specified components Vector( const Vector& v ); // initializes from existing vector double& operator[] ( const int& index ); // returns reference to the specified component (0-based indexing: x, y, z) const double& operator[] ( const int& index ) const; // returns const reference to the specified component (0-based indexing: x, y, z) Vector operator+( const Vector& v ) const; // addition Vector operator-( const Vector& v ) const; // subtraction Vector operator-( void ) const; // negation Vector operator*( const double& c ) const; // right scalar multiplication Vector operator/( const double& c ) const; // scalar division void operator+=( const Vector& v ); // addition / assignment void operator-=( const Vector& v ); // subtraction / assignment void operator*=( const double& c ); // scalar multiplication / assignment void operator/=( const double& c ); // scalar division / assignment double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Vector unit( void ) const; // returns unit vector void normalize( void ); // divides by Euclidean length Vector abs( void ) const; // returns vector containing magnitude of each component double x, y, z; // components }; Vector operator* ( const double& c, const Vector& v ); // left scalar multiplication double dot( const Vector& u, const Vector& v ); // dot product (a.k.a. inner or scalar product) Vector cross( const Vector& u, const Vector& v ); // cross product std::ostream& operator << (std::ostream& os, const Vector& o); // prints components } #endif ================================================ FILE: Elasticity/include/Vertex.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vertex.h // ----------------------------------------------------------------------------- // // Vertex stores attributes associated with a mesh edge. The iterator he // points to its "outgoing" halfedge. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_VERTEX_H #define DDG_VERTEX_H #include "Vector.h" #include "Types.h" namespace DDG { class Vertex { public: HalfEdgeIter he; // points to the "outgoing" halfedge Vector position; // location of vertex in Euclidean 3-space int index; // unique integer ID in the range 0, ..., nVertices-1 bool tag; // true if vertex is selected by the user; false otherwise Vertex() : index(0), tag(false) { } double area( void ) const; // returns the barycentric area associated with this vertex Vector normal( void ) const; // returns the vertex normal bool isIsolated( void ) const; // returns true if the vertex is not contained in any face or edge; false otherwise int valence( void ) const; // returns the number of incident faces / edges void toggleTag(); // toggle vertex tag }; } #endif ================================================ FILE: Elasticity/include/Viewer.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Viewer.h // ----------------------------------------------------------------------------- // // Viewer provides a graphical user interface (GUI) for inspecting and // interacting with a Mesh object. Viewer methods are static in order // to make them compatible with GLUT callbacks. // #ifndef DDG_VIEWER_H #define DDG_VIEWER_H #include #include "Mesh.h" #include "Camera.h" #include "Shader.h" namespace DDG { class Viewer { public: static void init( void ); // displays the viewer until the program ends static Mesh mesh, initial, source, target; // surface mesh visualized by Viewer protected: // init static void initGLUT( void ); static void initGLSL( void ); // GLUT callbacks static void display( void ); static void idle( void ); static void keyboard( unsigned char c, int x, int y ); static void special( int i, int x, int y ); static void mouse( int button, int state, int x, int y ); static void motion( int x, int y ); static void menu( int value ); static void view( int value ); // menu functions static void mProcess( void ); static void mResetMesh( void ); static void mWriteMesh( void ); static void mExit( void ); static void mWireframe( void ); static void mZoomIn( void ); static void mZoomOut( void ); static void mScreenshot( void ); static void mIncreaseStep( void ); static void mDecreaseStep( void ); static void mRenderInitial( void ); // unique identifiers for menus enum { menuProcess, menuResetMesh, menuWriteMesh, menuExit, menuWireframe, menuZoomIn, menuZoomOut, menuScreenshot, menuIncreaseStep, menuDecreaseStep, menuRenderInitial }; // draw routines static void setGL( void ); static void setLighting( void ); static void setMeshMaterial( void ); static void callDisplayList( void ); static void updateDisplayList( void ); static void drawScene( void ); static void drawPolygons( const Mesh& surf ); static void drawWireframe( const Mesh& surf ); static void storeViewerState( void ); static void restoreViewerState( void ); static int windowSize[2]; static bool renderWireframe; // draw wireframe static bool renderInitial; // draw initial mesh static Camera camera; // keeps track of view state static GLuint surfaceDL; // display list for mesh static Shader shader; // shader used to determine appearance of surface static bool animate; // whether we are iterating or not static double step; static double delta; // fairing time step }; } #endif ================================================ FILE: Elasticity/obj/.empty ================================================ ================================================ FILE: Elasticity/shaders/fragment.glsl ================================================ uniform vec3 eye; uniform vec3 light; varying vec3 position; varying vec3 normal; float diffuse( vec3 N, vec3 L ) { return max( 0., dot( N, L )); } float specular( vec3 N, vec3 L, vec3 E ) { const float shininess = 8.; vec3 R = 2.*dot(L,N)*N - L; return pow( max( 0., dot( R, E )), shininess ); } float fresnel( vec3 N, vec3 E ) { const float sharpness = 10.; float NE = max( 0., dot( N, E )); return pow( sqrt( 1. - NE*NE ), sharpness ); } void main() { vec3 N = normalize( normal ); vec3 L = normalize( light - position ); vec3 E = normalize( eye - position ); vec3 R = 2.*dot(L,N)*N - L; vec3 one = vec3( 1., 1., 1. ); gl_FragColor.rgb = diffuse(N,L)*gl_Color.rgb + .5*specular(N,L,E)*one + .5*fresnel(N,E)*one; gl_FragColor.a = 1.; } ================================================ FILE: Elasticity/shaders/vertex.glsl ================================================ varying vec3 position; varying vec3 normal; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_FrontColor = gl_Color; position = gl_Vertex.xyz; normal = gl_Normal.xyz; } ================================================ FILE: Elasticity/src/Camera.cpp ================================================ #include #include #include using namespace std; #include "Camera.h" namespace DDG { Camera :: Camera( void ) : pClick( 1. ), pDrag( 1. ), pLast( 1. ), rLast( 1. ), momentum( 1. ), zoom( 1. ) {} Quaternion Camera :: clickToSphere( int x, int y ) { GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); int w = viewport[2]; int h = viewport[3]; Quaternion p( 0., 2. * (double) x / (double) w - 1., 2. * (double) y / (double) h - 1., 0. ); if( p.norm2() > 1. ) { p.normalize(); p.im().z = 0.; } else { p.im().z = sqrt( 1. - p.norm2() ); } return p; } Quaternion Camera :: currentRotation( void ) const { return ( pDrag * pClick.conj() ) * rLast; } void Camera :: setView( void ) const { Quaternion r = ( pDrag * pClick.conj() ) * rLast; double w = r[0]; double x = r[1]; double y = r[2]; double z = r[3]; GLdouble M[16] = { 1.-2.*y*y-2.*z*z, 2.*x*y+2.*w*z, 2.*x*z-2.*w*y, 0., 2.*x*y-2.*w*z, 1.-2.*x*x-2.*z*z, 2.*y*z+2.*w*x, 0., 2.*x*z+2.*w*y, 2.*y*z-2.*w*x, 1.-2.*x*x-2.*y*y, 0., 0., 0., 0., 1. }; glMatrixMode( GL_MODELVIEW ); glMultMatrixd( M ); } void Camera :: mouse( int button, int state, int x, int y ) { if( state == GLUT_DOWN ) { pClick = pDrag = pLast = clickToSphere( x, y ); momentum = 1.; } if( state == GLUT_UP ) { double timeSinceDrag = ( clock() - tLast ) / (double) CLOCKS_PER_SEC; if( timeSinceDrag < .1 ) { momentum = pDrag * pLast.conj(); momentum = ( .03 * momentum + .97 ).unit(); } else { momentum = 1.; } rLast = pDrag * pClick.conj() * rLast; pClick = pDrag = 1.; } } void Camera :: motion( int x, int y ) { tLast = clock(); pLast = pDrag; pDrag = clickToSphere( x, y ); } void Camera :: idle( void ) { // get time since last idle event static int t0 = clock(); int t1 = clock(); double dt = (t1-t0) / (double) CLOCKS_PER_SEC; rLast = momentum * rLast; momentum = ( (1.-.5*dt) * momentum + .5*dt ).unit(); zoom += vZoom*dt; vZoom *= max( 0., 1.-5.*dt ); t0 = t1; } void Camera :: zoomIn( void ) { vZoom -= 0.5; } void Camera :: zoomOut( void ) { vZoom += 0.5; } } ================================================ FILE: Elasticity/src/Complex.cpp ================================================ #include "Complex.h" #include #include using namespace std; namespace DDG { Complex::Complex( double a, double b ) // constructs number a+bi : re( a ), im( b ) {} void Complex::operator+=( const Complex& z ) // add z { re += z.re; im += z.im; } void Complex::operator-=( const Complex& z ) // subtract z { re -= z.re; im -= z.im; } void Complex::operator*=( const Complex& z ) // Complex multiply by z { double a = re; double b = im; double c = z.re; double d = z.im; re = a*c-b*d; im = a*d+b*c; } void Complex::operator*=( double r ) // scalar multiply by r { re *= r; im *= r; } void Complex::operator/=( double r ) // scalar divide by r { re /= r; im /= r; } void Complex::operator/=( const Complex& z ) // scalar divide by r { *this *= z.inv(); } Complex Complex::operator-( void ) const { return Complex( -re, -im ); } Complex Complex::conj( void ) const // returns Complex conjugate { return Complex( re, -im ); } Complex Complex::inv( void ) const // returns inverse { return this->conj() / this->norm2(); } double Complex::arg( void ) const // returns argument { return atan2( im, re ); } double Complex::norm( void ) const // returns norm { return sqrt( re*re + im*im ); } double Complex::norm2( void ) const // returns norm squared { return re*re + im*im; } Complex Complex::unit( void ) const // returns complex number with unit norm and same modulus { return *this / this->norm(); } Complex Complex::exponential( void ) const // complex exponentiation { return exp( re ) * Complex( cos( im ), sin( im )); } Complex operator+( const Complex& z1, const Complex& z2 ) // binary addition { Complex z = z1; z += z2; return z; } Complex operator-( const Complex& z1, const Complex& z2 ) // binary subtraction { Complex z = z1; z -= z2; return z; } Complex operator*( const Complex& z1, const Complex& z2 ) // binary Complex multiplication { Complex z = z1; z *= z2; return z; } Complex operator*( const Complex& z, double r ) // right scalar multiplication { Complex zr = z; zr *= r; return zr; } Complex operator*( double r, const Complex& z ) // left scalar multiplication { return z*r; } Complex operator/( const Complex& z, double r ) // scalar division { Complex zr = z; zr /= r; return zr; } Complex operator/( const Complex& z1, const Complex& z2 ) // complex division { Complex z = z1; z /= z2; return z; } double dot( const Complex& z1, const Complex& z2 ) { return z1.re*z2.re + z1.im*z2.im; } double cross( const Complex& z1, const Complex& z2 ) { return z1.re*z2.im - z1.im*z2.re; } std::ostream& operator<<( std::ostream& os, const Complex& z ) // prints components { if( z.im > 0 ) { os << z.re << " + " << z.im << "i"; } else if( z.im < 0 ) { os << z.re << " - " << -z.im << "i"; } else { os << z.re; } return os; } } ================================================ FILE: Elasticity/src/DenseMatrix.cpp ================================================ #include "DenseMatrix.h" namespace DDG { template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i] = data[i]; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_COMPLEX, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i*2+0] = data[i].re; x[i*2+1] = data[i].im; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { assert( nColumns() == 1 ); if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } cData = cholmod_l_allocate_dense( m*4, 1, m*4, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { x[i*4+k] = data[i][k]; } } return cData; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = x[i]; } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = Complex( x[i*2+0], x[i*2+1] ); } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); assert( B->ncol == 1 ); assert( B->nrow%4 == 0 ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow/4; n = 1; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m; i++ ) { data[i] = Quaternion( x[i*4+0], x[i*4+1], x[i*4+2], x[i*4+3] ); } return *this; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 3; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { double x = o(i,j); if( x == 0. ) { os << " 0"; for( int k = 0; k < p+6; k++ ) { os << " "; } } else if( x > 0. ) { os << " " << x << " "; } else { os << x << " "; } } os << "]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { Complex z = o(i,j); if( z.re == 0. ) { os << " 0"; for( int k = 0; k < p+5; k++ ) { os << " "; } } else if( z.re > 0. ) { os << " " << z.re; } else { os << z.re; } if( z.im == 0 ) { os << " "; } else if( z.im >= 0 ) { os << "+"; } else { os << "-"; } if( z.im == 0. ) { for( int k = 0; k < p+8; k++ ) { os << " "; } } else { os << abs( z.im ) << "i "; } } os << " ]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "["; for( int j = 0; j < o.nColumns(); j++ ) { Quaternion q = o(i,j); os << " " << q; } os << " ]" << endl; } return os; } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i] = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i].re = 2.*unitRand() - 1.; data[i].im = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { data[i][k] = 2.*unitRand() - 1.; } } } } ================================================ FILE: Elasticity/src/DenseMatrix.inl ================================================ #include #include #include #include using namespace std; #include "DenseMatrix.h" #include "LinearContext.h" #include "Quaternion.h" #include "SparseMatrix.h" #include "Utility.h" namespace DDG { extern LinearContext context; template DenseMatrix :: DenseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) { data.resize( m*n ); zero(); } template DenseMatrix :: DenseMatrix( const DenseMatrix& A ) // copy constructor : cData( NULL ) { *this = A; } template DenseMatrix :: ~DenseMatrix( void ) // destructor { if( cData != NULL ) { cholmod_l_free_dense( &cData, context ); } } template DenseMatrix DenseMatrix :: transpose( void ) const { const DenseMatrix& A( *this ); DenseMatrix AT( n, m ); for( int i = 0; i < n; i++ ) for( int j = 0; j < m; j++ ) { AT(i,j) = A(j,i).conj(); } return AT; } template SparseMatrix DenseMatrix::sparse( void ) // converts to a sparse matrix { SparseMatrix B; B = cholmod_l_dense_to_sparse( this->to_cholmod(), true, context ); return B; } template DenseMatrix DenseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); DenseMatrix AB( A.nRows(), B.nColumns() ); for( int i = 0; i < A.nRows(); i++ ) for( int j = 0; j < B.nColumns(); j++ ) for( int k = 0; k < A.nColumns(); k++ ) { AB( i, j ) += A( i, k ) * B( k, j ); } return AB; } template void DenseMatrix :: operator*=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c; } } template void DenseMatrix :: operator/=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c.inv(); } } template DenseMatrix DenseMatrix :: operator+( const DenseMatrix& B ) const // returns sum of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) + B(i,j); } return C; } template void DenseMatrix :: operator+=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) += B(i,j); } } template DenseMatrix DenseMatrix :: operator-( const DenseMatrix& B ) const // returns difference of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) - B(i,j); } return C; } template void DenseMatrix :: operator-=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) -= B(i,j); } } template DenseMatrix operator*( const T& c, const DenseMatrix& A ) { DenseMatrix cA = A; cA *= c; return cA; } template DenseMatrix operator*( const DenseMatrix& A, double c ) { return c*A; } template DenseMatrix operator/( const DenseMatrix& A, double c ) { DenseMatrix Ac = A; Ac /= c; return Ac; } template const DenseMatrix& DenseMatrix :: operator=( const DenseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template int DenseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int DenseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int DenseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void DenseMatrix :: zero( const T& val ) // sets all elements to val { for( int i = 0; i < m*n; i++ ) { data[i] = val; } } template double DenseMatrix :: norm( NormType type ) const { double r = 0.; if( type == lInfinity ) { for( int i = 0; i < m*n; i++ ) { r = max( r, data[i].norm() ); } } else if( type == lOne ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm(); } } else if( type == lTwo ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm2(); } r = sqrt( r ); } return r; } template void DenseMatrix :: normalize( void ) // divides by l2 norm { *this /= norm( lTwo ); } template T& DenseMatrix :: operator()( int row, int col ) { return data[row+m*col]; } template T DenseMatrix :: operator()( int row, int col ) const { return data[row+m*col]; } template T& DenseMatrix :: operator()( int index ) { return data[index]; } template T DenseMatrix :: operator()( int index ) const { return data[index]; } template T DenseMatrix::sum( void ) const // returns the sum of all entries { T total( 0., 0. ); for( int i = 0; i < m*n; i++ ) { total += data[i]; } return total; } template void DenseMatrix :: removeMean( void ) { T mean = 0.; int N = m*n; for( int i = 0; i < N; i++ ) { mean += data[i]; } mean /= (double) N; for( int i = 0; i < N; i++ ) { data[i] -= mean; } } template T dot( const DenseMatrix& x, const DenseMatrix& y ) // returns Euclidean inner product of x and y { return ( x.transpose() * y )(0); } template DenseMatrix DenseMatrix::operator-( void ) const // returns additive inverse of this matrix { const DenseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { B( i, j ) = -A( i, j ); } return B; } template T inner( const DenseMatrix& x, const DenseMatrix& y ) // standard inner product { T sum = 0.; assert( x.nRows() == y.nRows() && x.nColumns() == y.nColumns() ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * y(i); } return sum; } template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ) // inner product with respect a diagonal inner // product B represented as a dense vector { T sum = 0.; assert( x.nRows() == y.nRows() && x.nRows() == B.nRows() && x.nColumns() == 1 && B.nColumns() == 1 && y.nColumns() == 1 ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * B(i) * y(i); } return sum; } } ================================================ FILE: Elasticity/src/DiscreteExteriorCalculus.inl ================================================ #include "DiscreteExteriorCalculus.h" namespace DDG { template void HodgeStar0Form :: build( const Mesh& mesh, SparseMatrix& star0 ) // builds a diagonal matrix mapping primal discrete 0-forms // to dual discrete 2-forms { int nV = mesh.vertices.size(); star0 = SparseMatrix( nV, nV ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { int i = v->index; star0( i, i ) = v->area(); } } template void HodgeStar1Form :: build( const Mesh& mesh, SparseMatrix& star1 ) // builds a diagonal matrix mapping primal discrete 1-forms // to dual discrete 1-forms { int nE = mesh.edges.size(); star1 = SparseMatrix( nE, nE ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // get the cotangents of the two angles opposite this edge double cotAlpha = e->he->cotan(); double cotBeta = e->he->flip->cotan(); int i = e->index; star1( i, i ) = ( cotAlpha + cotBeta ) / 2.; } } template void HodgeStar2Form :: build( const Mesh& mesh, SparseMatrix& star2 ) // builds a diagonal matrix mapping primal discrete 2-forms // to dual discrete 2-forms { int nF = mesh.faces.size(); star2 = SparseMatrix( nF, nF ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { int i = f->index; star2( i, i ) = 1. / f->area(); } } template< class T > void ExteriorDerivative0Form :: build( const Mesh& mesh, SparseMatrix& d0 ) { int nV = mesh.vertices.size(); int nE = mesh.edges.size(); d0 = SparseMatrix( nE, nV ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // the row index is the index of the edge int r = e->index; // the column indices are the indices of the two // edge vertices -- orientation is determined by // the orientation of the edge's first half edge int ci = e->he->vertex->index; int cj = e->he->flip->vertex->index; d0( r, ci ) = -1.; d0( r, cj ) = 1.; } } template< class T > void ExteriorDerivative1Form :: build( const Mesh& mesh, SparseMatrix& d1 ) { int nE = mesh.edges.size(); int nF = mesh.faces.size(); d1 = SparseMatrix( nF, nE ); // visit each face for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { // the row index is the index of the face int r = f->index; // visit all edges of this face HalfEdgeCIter he = f->he; do { // the column index is the index of the current edge int c = he->edge->index; // relative orientation is determined by checking if // the current half edge is the first half edge of its // corresponding edge double s = ( he->edge->he == he ? 1. : -1. ); // set the entry for this edge d1( r, c ) = s; he = he->next; } while( he != f->he ); } } } ================================================ FILE: Elasticity/src/Edge.cpp ================================================ #include "Edge.h" #include "Mesh.h" namespace DDG { } ================================================ FILE: Elasticity/src/Face.cpp ================================================ #include "Face.h" #include "Mesh.h" #include "Vector.h" namespace DDG { double Face::area( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).norm() / 2.; } Vector Face::normal( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).unit(); } bool Face::isBoundary( void ) const { return he->onBoundary; } Vector Face :: circumcenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector n = he->rotatedEdge(); double h = 0.5*he->cotan(); return 0.5*(p0+p1) + h*n; } Vector Face :: barycenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return (p0 + p1 + p2)/3.; } } ================================================ FILE: Elasticity/src/HalfEdge.cpp ================================================ #include "HalfEdge.h" #include "Mesh.h" namespace DDG { double HalfEdge :: cotan( void ) const { if( onBoundary ) return 0.0; Vector p0 = next->next->vertex->position; Vector p1 = vertex->position; Vector p2 = next->vertex->position; Vector u = p1-p0; Vector v = p2-p0; return dot( u, v ) / cross( u, v ).norm(); } Vector HalfEdge :: rotatedEdge( void ) const { if( onBoundary ) return Vector(); Vector n = face->normal(); Vector p0 = vertex->position; Vector p1 = flip->vertex->position; return cross( n, p1-p0 ); } } ================================================ FILE: Elasticity/src/Image.cpp ================================================ #include #include #include #include using namespace std; #include "Image.h" namespace DDG { Image :: Image( int width, int height ) : w( width ), h( height ), pixels( w*h*3 ) {} float& Image :: operator()( int x, int y ) // accesses pixel (x,y) { return pixels[ x + y*w ]; } const float& Image :: operator()( int x, int y ) const // accesses pixel (x,y) { return pixels[ x + y*w ]; } float Image :: sample( float x, float y ) const // samples image at (x,y) using bilinear filtering { const Image& I( *this ); float ax = x - floor( x ); float ay = y - floor( y ); float bx = 1. - ax; float by = 1. - ay; int x0 = (int) floor( x ); int y0 = (int) floor( y ); int x1 = x0 + 1; int y1 = y0 + 1; clamp( x0, y0 ); clamp( x1, y1 ); return by * ( bx * I(x0,y0) + ax * I(x1,y0) ) + ay * ( bx * I(x0,y1) + ax * I(x1,y1) ) ; } int Image :: width( void ) const // returns image width { return w; } int Image :: height( void ) const // returns image height { return h; } class TGAHeader // header format for Truevision TGA images { public: char idFieldSize; char colorMapType; char dataTypeCode; short colorMapOrigin; short colorMapLength; char colorMapEntrySize; short xOrigin; short yOrigin; short width; short height; char bitsPerPixel; char imageSpecification; }; void Image :: read( const char* filename ) // loads an image file in Truevision TGA format // (must be uncompressed RGB image with 24 or 32 bits per pixel) { ifstream in( filename, ios_base::binary ); if( !in.is_open() ) { cerr << "Error: could not open file " << filename << " for input!" << endl; exit( 1 ); } // read header TGAHeader header; in.read( (char*) &(header.idFieldSize), 1 ); in.read( (char*) &(header.colorMapType), 1 ); in.read( (char*) &(header.dataTypeCode), 1 ); in.read( (char*) &(header.colorMapOrigin), 2 ); in.read( (char*) &(header.colorMapLength), 2 ); in.read( (char*) &(header.colorMapEntrySize), 1 ); in.read( (char*) &(header.xOrigin), 2 ); in.read( (char*) &(header.yOrigin), 2 ); in.read( (char*) &(header.width), 2 ); in.read( (char*) &(header.height), 2 ); in.read( (char*) &(header.bitsPerPixel), 1 ); in.read( (char*) &(header.imageSpecification), 1 ); w = header.width; h = header.height; // validate data type const char uncompressedRGB = 2; if( header.dataTypeCode != uncompressedRGB || ( header.bitsPerPixel != 24 && header.bitsPerPixel != 32 )) { cerr << "Error: input must be uncompressed RGB image with 24 or 32 bits per pixel." << endl; exit( 1 ); } // read identification field (unused) vector idField( header.idFieldSize ); in.read( &idField[0], header.idFieldSize ); // read color map data (unused) if( header.colorMapType == 1 ) { int bytesPerEntry = header.colorMapEntrySize / 8; int colorMapSize = header.colorMapLength * bytesPerEntry; vector colorMapData( colorMapSize ); in.read( &colorMapData[0], colorMapSize ); } // read pixel data int n = w*h*header.bitsPerPixel/8; vector pixelData( n ); in.read( (char*) &pixelData[0], n ); // convert pixel data to floating point pixels.resize( n ); for( int i = 0; i < n; i++ ) { pixels[i] = (double) pixelData[i] / 255.; } } void Image :: write( const char* filename ) const // writes an image file in Truevision TGA format // (uncompressed RGB image with 24 bits per pixel) { ofstream out( filename, ios_base::binary ); if( !out.is_open() ) { cerr << "Error: could not open file " << filename << " for output!" << endl; exit( 1 ); } TGAHeader header; header.idFieldSize = 0; header.colorMapType = 0; header.dataTypeCode = 2; header.colorMapOrigin = 0; header.colorMapLength = 0; header.colorMapEntrySize = 0; header.xOrigin = 0; header.yOrigin = 0; header.width = w; header.height = h; header.bitsPerPixel = 24; header.imageSpecification = 0; // write header out.write( (char*) &(header.idFieldSize), 1 ); out.write( (char*) &(header.colorMapType), 1 ); out.write( (char*) &(header.dataTypeCode), 1 ); out.write( (char*) &(header.colorMapOrigin), 2 ); out.write( (char*) &(header.colorMapLength), 2 ); out.write( (char*) &(header.colorMapEntrySize), 1 ); out.write( (char*) &(header.xOrigin), 2 ); out.write( (char*) &(header.yOrigin), 2 ); out.write( (char*) &(header.width), 2 ); out.write( (char*) &(header.height), 2 ); out.write( (char*) &(header.bitsPerPixel), 1 ); out.write( (char*) &(header.imageSpecification), 1 ); // convert pixel data from floating point vector pixelData( w*h*3 ); for( int i = 0; i < w*h*3; i++ ) { pixelData[i] = (unsigned char)( pixels[i] * 255. ); } // write pixel data out.write( (char*) &pixelData[0], w*h*3 ); } void Image :: clamp( int& x, int& y ) const // clamps coordinates to range [0,w-1] x [0,h-1] { x = max( 0, min( w-1, x )); y = max( 0, min( h-1, y )); } } ================================================ FILE: Elasticity/src/LinearContext.cpp ================================================ #include "LinearContext.h" namespace DDG { // global context for linear solvers LinearContext context; LinearContext :: LinearContext( void ) // constructor { cholmod_l_start( &context ); } LinearContext :: ~LinearContext( void ) // destructor { cholmod_l_finish( &context ); } LinearContext :: operator cholmod_common*( void ) // allows LinearContext to be treated as a cholmod_common* { return &context; } } ================================================ FILE: Elasticity/src/LinearEquation.cpp ================================================ #include "LinearEquation.h" namespace DDG { LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ) // constructs a linear equation with the specified left- and right-hand side { LinearEquation eqn; eqn.lhs = lhs; eqn.rhs = rhs; return eqn; } } ================================================ FILE: Elasticity/src/LinearPolynomial.cpp ================================================ #include using namespace std; #include "LinearPolynomial.h" #include "Types.h" namespace DDG { LinearPolynomial :: LinearPolynomial( void ) : constantTerm( 0. ) {} LinearPolynomial :: LinearPolynomial( double c ) { *this = c; } LinearPolynomial :: LinearPolynomial( Variable& v ) { *this = v; } const LinearPolynomial& LinearPolynomial :: operator=( double c ) { linearTerms.clear(); constantTerm = c; return *this; } const LinearPolynomial& LinearPolynomial :: operator=( Variable& v ) { linearTerms.clear(); linearTerms[ &v ] = 1.; constantTerm = 0.; return *this; } void LinearPolynomial::operator+=( double c ) { constantTerm += c; } void LinearPolynomial::operator-=( double c ) { constantTerm -= c; } void LinearPolynomial::operator*=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second *= c; } constantTerm *= c; } void LinearPolynomial::operator/=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second /= c; } constantTerm /= c; } void LinearPolynomial::operator+=( Variable& v ) { LinearPolynomial p( v ); *this += p; } void LinearPolynomial::operator-=( Variable& v ) { LinearPolynomial p( v ); *this -= p; } void LinearPolynomial::operator+=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second += i->second; } else { linearTerms[i->first] = i->second; } } constantTerm += e.constantTerm; } void LinearPolynomial::operator-=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second -= i->second; } else { linearTerms[i->first] = -i->second; } } constantTerm -= e.constantTerm; } LinearPolynomial LinearPolynomial::operator-( void ) const { LinearPolynomial p = *this; for( TermIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { i->second = -i->second; } p.constantTerm = -p.constantTerm; return p; } double LinearPolynomial::evaluate( void ) const { double value = constantTerm; for( TermCIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { value += i->second * i->first->value; } return value; } LinearPolynomial operator+( double c, Variable& v ) { return c + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, double c ) { return LinearPolynomial(v) + c; } LinearPolynomial operator-( double c, Variable& v ) { return c - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, double c ) { return LinearPolynomial(v) - c; } LinearPolynomial operator*( double c, Variable& v ) { return LinearPolynomial(v) * c; } LinearPolynomial operator*( Variable& v, double c ) { return LinearPolynomial(v) * c; } LinearPolynomial operator/( Variable& v, double c ) { return LinearPolynomial(v) / c; } LinearPolynomial operator+( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) + LinearPolynomial(v2); } LinearPolynomial operator-( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) - LinearPolynomial(v2); } LinearPolynomial operator+( const LinearPolynomial& p, double c ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator+( double c, const LinearPolynomial& p ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, double c ) { LinearPolynomial difference = p; difference -= c; return difference; } LinearPolynomial operator-( double c, const LinearPolynomial& p ) { LinearPolynomial difference = -p; difference += c; return difference; } LinearPolynomial operator*( const LinearPolynomial& p, double c ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator*( double c, const LinearPolynomial& p ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator/( const LinearPolynomial& p, double c ) { LinearPolynomial quotient = p; quotient /= c; return quotient; } LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ) { return p + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) + p; } LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ) { return p - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) - p; } LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial sum = p; sum += q; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial difference = p; difference -= q; return difference; } ostream& operator<<( ostream& os, const LinearPolynomial& p ) { for( TermCIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { os << i->second << "*" << i->first->name << " + "; } os << p.constantTerm; return os; } } ================================================ FILE: Elasticity/src/LinearSystem.cpp ================================================ #include using namespace std; #include #include "LinearSystem.h" #include "LinearContext.h" #include "Types.h" namespace DDG { extern LinearContext context; void LinearSystem::clear( void ) // removes all equations from the system { equations.clear(); } void LinearSystem::push_back( const LinearEquation& e ) // appends the equation e to the sytem { equations.push_back( e ); } void LinearSystem::solve( void ) // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution { convertEquations(); indexVariables(); buildSparseMatrix(); buildRightHandSide(); computeSolution(); } void LinearSystem::convertEquations( void ) // converts each equation to its polynomial representation { currentEquations.clear(); for( EqnIter eqn = equations.begin(); eqn != equations.end(); eqn ++ ) { // move right-hand side to left-hand side LinearPolynomial p = eqn->lhs - eqn->rhs; // convert fixed variables to constants LinearPolynomial q( p.constantTerm ); for( TermIter t = p.linearTerms.begin(); t != p.linearTerms.end(); t ++ ) { const double& coefficient( t->second ); Variable& variable( *(t->first) ); // skip zeros if( coefficient == 0. ) continue; if( t->first->fixed ) { q += coefficient * variable.value; } else { q += coefficient * variable; } } if( q.linearTerms.size() > 0 ) { currentEquations.push_back( q ); } } nEquations = currentEquations.size(); } void LinearSystem::indexVariables( void ) // assign a unique index to each variable remaining in one of the polynomials { index.clear(); nVariables = 0; for( PolyCIter p = currentEquations.begin(); p != currentEquations.end(); p ++ ) { for( TermCIter t = p->linearTerms.begin(); t != p->linearTerms.end(); t ++ ) { IndexIter j = index.find( t->first ); // if we haven't seen this variable // before, assign it a unique index if( j == index.end() ) { index[ t->first ] = nVariables; nVariables++; } } } } void LinearSystem::buildSparseMatrix( void ) // build the sparse matrix representation of our current system { A = SparseMatrix( nEquations, nVariables ); for( int i = 0; i < nEquations; i++ ) { for( TermCIter t = currentEquations[i].linearTerms.begin(); t != currentEquations[i].linearTerms.end(); t ++ ) { int j = index[ t->first ]; A(i,j) = t->second; } } } void LinearSystem::buildRightHandSide( void ) // build the data vector for our current system { b = DenseMatrix( nEquations, 1 ); for( int i = 0; i < nEquations; i++ ) { b(i) = -currentEquations[i].constantTerm; } } void LinearSystem::computeSolution( void ) { // solve linear system Ax=b x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); // put solution values in variables for( IndexIter i = index.begin(); i != index.end(); i ++ ) { i->first->value = x( i->second ); } } } ================================================ FILE: Elasticity/src/Mesh.cpp ================================================ #include #include #include "Mesh.h" #include "MeshIO.h" #include "DiscreteExteriorCalculus.h" using namespace std; namespace DDG { Mesh :: Mesh( void ) {} Mesh :: Mesh( const Mesh& mesh ) { *this = mesh; } class HalfEdgeIterCompare { public: bool operator()( const HalfEdgeIter& i, const HalfEdgeIter& j ) const { return &*i < &*j; } }; class HalfEdgeCIterCompare { public: bool operator()( const HalfEdgeCIter& i, const HalfEdgeCIter& j ) const { return &*i < &*j; } }; class VertexIterCompare { public: bool operator()( const VertexIter& i, const VertexIter& j ) const { return &*i < &*j; } }; class VertexCIterCompare { public: bool operator()( const VertexCIter& i, const VertexCIter& j ) const { return &*i < &*j; } }; class FaceIterCompare { public: bool operator()( const FaceIter& i, const FaceIter& j ) const { return &*i < &*j; } }; class FaceCIterCompare { public: bool operator()( const FaceCIter& i, const FaceCIter& j ) const { return &*i < &*j; } }; class EdgeIterCompare { public: bool operator()( const EdgeIter& i, const EdgeIter& j ) const { return &*i < &*j; } }; class EdgeCIterCompare { public: bool operator()( const EdgeCIter& i, const EdgeCIter& j ) const { return &*i < &*j; } }; const Mesh& Mesh :: operator=( const Mesh& mesh ) { map< HalfEdgeCIter, HalfEdgeIter, HalfEdgeCIterCompare > halfedgeOldToNew; map< VertexCIter, VertexIter, VertexCIterCompare > vertexOldToNew; map< EdgeCIter, EdgeIter, EdgeCIterCompare > edgeOldToNew; map< FaceCIter, FaceIter, FaceCIterCompare > faceOldToNew; // copy geometry from the original mesh and create a // map from pointers in the original mesh to // those in the new mesh halfedges.clear(); for( HalfEdgeCIter he = mesh.halfedges.begin(); he != mesh.halfedges.end(); he++ ) halfedgeOldToNew[ he ] = halfedges.insert( halfedges.end(), *he ); vertices.clear(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) vertexOldToNew[ v ] = vertices.insert( vertices.end(), *v ); edges.clear(); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e++ ) edgeOldToNew[ e ] = edges.insert( edges.end(), *e ); faces.clear(); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) faceOldToNew[ f ] = faces.insert( faces.end(), *f ); // "search and replace" old pointers with new ones for( HalfEdgeIter he = halfedges.begin(); he != halfedges.end(); he++ ) { he->next = halfedgeOldToNew[ he->next ]; he->flip = halfedgeOldToNew[ he->flip ]; he->vertex = vertexOldToNew[ he->vertex ]; he->edge = edgeOldToNew[ he->edge ]; he->face = faceOldToNew[ he->face ]; } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) v->he = halfedgeOldToNew[ v->he ]; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) e->he = halfedgeOldToNew[ e->he ]; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) f->he = halfedgeOldToNew[ f->he ]; return *this; } int Mesh::read( const string& filename ) { inputFilename = filename; ifstream in( filename.c_str() ); if( !in.is_open() ) { cerr << "Error reading from mesh file " << filename << endl; return 1; } int rval; if( !( rval = MeshIO::read( in, *this ))) { indexElements(); normalize(); } return rval; } int Mesh::write( const string& filename ) const // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error { ofstream out( filename.c_str() ); if( !out.is_open() ) { cerr << "Error writing to mesh file " << filename << endl; return 1; } MeshIO::write( out, *this ); return 0; } bool Mesh::reload( void ) { return read( inputFilename ); } void Mesh::normalize( void ) { // compute center of mass Vector c( 0., 0., 0. ); for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { c += v->position; } c /= (double) vertices.size(); // translate to origin for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position -= c; } // rescale such that the mesh sits inside the unit ball double rMax = 0.; for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { rMax = max( rMax, v->position.norm() ); } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position /= rMax; } } void Mesh::indexElements( void ) { int nV = 0; for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->index = nV; nV++; } int nE = 0; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) { e->index = nE; nE++; } int nF = 0; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) { f->index = nF; nF++; } } double Mesh::area( void ) const { double sum = 0.0; for( FaceCIter f = faces.begin(); f != faces.end(); f ++ ) { sum += f->area(); } return sum; } double Mesh::meanEdgeLength( void ) const { double sum = 0; for( EdgeCIter e = edges.begin(); e != edges.end(); e ++) { VertexIter v0 = e->he->vertex; VertexIter v1 = e->he->flip->vertex; sum += (v0->position - v1->position).norm(); } return sum / edges.size(); } } ================================================ FILE: Elasticity/src/MeshIO.cpp ================================================ #include #include #include #include #include "MeshIO.h" #include "Mesh.h" using namespace std; namespace DDG { class Index { public: Index( void ) {} Index( int p, int t, int n ) : position( p ), texcoord( t ), normal( n ) {} bool operator<( const Index& i ) const { if( position < i.position ) return true; if( position > i.position ) return false; if( texcoord < i.texcoord ) return true; if( texcoord > i.texcoord ) return false; if( normal < i.normal ) return true; if( normal > i.normal ) return false; return false; } int position; int texcoord; int normal; }; class MeshData { public: std::vector positions; std::vector texcoords; std::vector normals; std::vector< std::vector< Index > > indices; }; int MeshIO :: read( istream& in, Mesh& mesh ) // reads a mesh from a valid, open input stream in { MeshData data; if( readMeshData( in, data )) { return 1; } if( buildMesh( data, mesh )) { return 1; } return 0; } void MeshIO :: write( ostream& out, const Mesh& mesh ) // writes a mesh to a valid, open output stream out { int currentIndex = 1; map vertexIndex; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) { out << "v " << v->position.x << " " << v->position.y << " " << v->position.z << endl; vertexIndex[ v ] = currentIndex; currentIndex++; } for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeIter he = f->he; for( int j = 0; j < 3; j++ ) { out << "vt " << he->texcoord.x << " " << he->texcoord.y << endl; he = he->next; } } for( size_t i = 0; i < mesh.faces.size(); i++ ) { const Face& f( mesh.faces[i] ); HalfEdgeIter he = f.he; out << "f "; int j = 0; do { out << vertexIndex[ he->vertex ] << "/" << 1+(i*3+j) << " "; he = he->next; j++; } while( he != f.he ); out << endl; } } int MeshIO :: readMeshData( istream& in, MeshData& data ) { string line; while( getline( in, line )) { stringstream ss( line ); string token; ss >> token; if( token == "v" ) { readPosition( ss, data ); continue; } // vertex if( token == "vt" ) { readTexCoord( ss, data ); continue; } // texture coordinate if( token == "vn" ) { readNormal ( ss, data ); continue; } // vertex normal if( token == "f" ) { readFace ( ss, data ); continue; } // face if( token[0] == '#' ) continue; // comment if( token == "o" ) continue; // object name if( token == "g" ) continue; // group name if( token == "s" ) continue; // smoothing group if( token == "mtllib" ) continue; // material library if( token == "usemtl" ) continue; // material if( token == "" ) continue; // empty string cerr << "Error: does not appear to be a valid Wavefront OBJ file!" << endl; cerr << "(Offending line: " << line << ")" << endl; return 1; } return 0; } void MeshIO :: preallocateMeshElements( const MeshData& data, Mesh& mesh ) { // count the number of edges set< pair > edges; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { for( unsigned int I = 0; I < f->size(); I++ ) { int J = (I+1) % f->size(); int i = (*f)[I].position; int j = (*f)[J].position; if( i > j ) swap( i, j ); edges.insert( pair( i, j )); } } int nV = data.positions.size(); int nE = edges.size(); int nF = data.indices.size(); int nHE = 2*nE; int chi = nV - nE + nF; int nB = max( 0, 2 - chi ); // (conservative approximation of number of boundary cycles) mesh.halfedges.clear(); mesh.vertices.clear(); mesh.edges.clear(); mesh.faces.clear(); mesh.boundaries.clear(); mesh.halfedges.reserve( nHE ); mesh.vertices.reserve( nV ); mesh.edges.reserve( nE ); mesh.faces.reserve( nF ); mesh.boundaries.reserve( nB ); } extern vector isolated; // all isolated vertices point to isolated.begin() int MeshIO :: buildMesh( const MeshData& data, Mesh& mesh ) { map< pair< int, int >, int > edgeCount; map< pair< int, int >, HalfEdgeIter > existingHalfEdges; map< int, VertexIter > indexToVertex; map< HalfEdgeIter, bool > hasFlipEdge; preallocateMeshElements( data, mesh ); // allocate a vertex for each position in the data and construct // a map from vertex indices to vertex pointers for( unsigned int i = 0; i < data.positions.size(); i++ ) { VertexIter newVertex = mesh.vertices.insert( mesh.vertices.end(), Vertex() ); newVertex->position = data.positions[ i ]; newVertex->he = isolated.begin(); indexToVertex[ i ] = newVertex; } // insert each face into the mesh int faceIndex = 0; bool degenerateFaces = false; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { int N = f->size(); // print an error if the face is degenerate if( N < 3 ) { cerr << "Error: face " << faceIndex << " is degenerate (fewer than three vertices)!" << endl; degenerateFaces = true; continue; } // create a new face FaceIter newFace = mesh.faces.insert( mesh.faces.end(), Face()); // create a new half edge for each edge of the current face vector< HalfEdgeIter > hes( N ); for( int i = 0; i < N; i++ ) { hes[ i ] = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); } // initialize these new halfedges for( int i = 0; i < N; i++ ) { // the current halfedge goes from vertex a to vertex b int a = (*f)[ i ].position; int b = (*f)[ (i+1) % N ].position; // set current halfedge's attributes hes[ i ]->next = hes[ (i+1) % N ]; hes[ i ]->vertex = indexToVertex[ a ]; int t = (*f)[i].texcoord; if( t >= 0 ) hes[ i ]->texcoord = data.texcoords[ t ]; else hes[ i ]->texcoord = Vector( 0., 0., 0. ); hes[ i ]->onBoundary = false; // keep track of which halfedges have flip edges defined (for detecting boundaries) hasFlipEdge[ hes[ i ]] = false; // point vertex a at the current halfedge indexToVertex[ a ]->he = hes[ i ]; // point the new face and this half edge to each-other hes[ i ]->face = newFace; newFace->he = hes[ i ]; // if we've created an edge between a and b in the past, it is the // flip edge of the current halfedge if( a > b ) swap( a, b ); if( existingHalfEdges.find( pair( a, b )) != existingHalfEdges.end()) { hes[ i ]->flip = existingHalfEdges[ pair( a, b ) ]; hes[ i ]->flip->flip = hes[ i ]; hes[ i ]->edge = hes[ i ]->flip->edge; hasFlipEdge[ hes[ i ]] = true; hasFlipEdge[ hes[ i ]->flip ] = true; } else // otherwise, create an edge connected to the current halfedge { hes[ i ]->edge = mesh.edges.insert( mesh.edges.end(), Edge()); hes[ i ]->edge->he = hes[i]; edgeCount[ pair( a, b ) ] = 0; } // record the fact that we've created a halfedge from a to b existingHalfEdges[ pair( a, b ) ] = hes[ i ]; // check for nonmanifold edges edgeCount[ pair( a, b ) ]++; if( edgeCount[ pair( a, b ) ] > 2 ) { cerr << "Error: edge (" << a << ", " << b << ") is nonmanifold (more than two faces sharing a single edge)!" << endl; return 1; } } faceIndex++; } // give up now if there were degenerate faces if( degenerateFaces ) { return 1; } // insert extra faces for each boundary cycle for( HalfEdgeIter currentHE = mesh.halfedges.begin(); currentHE != mesh.halfedges.end(); currentHE ++ ) { // if we find a halfedge with no flip edge defined, create // a new face and link it to the corresponding boundary cycle if( !hasFlipEdge[ currentHE ] ) { // create a new face FaceIter newBoundary = mesh.boundaries.insert( mesh.boundaries.end(), Face()); // walk along this boundary cycle vector boundaryCycle; HalfEdgeIter he = currentHE; do { // create a new halfedge on the boundary face HalfEdgeIter newHE = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); // mark only the halfedge on the boundary face as being on the boundary newHE->onBoundary = true; // link the current halfedge in the cycle to its new flip edge he->flip = newHE; // grab the next halfedge along the boundary by finding // the next halfedge around the current vertex that doesn't // have a flip edge defined HalfEdgeIter nextHE = he->next; while( hasFlipEdge[ nextHE ] ) { nextHE = nextHE->flip->next; } // set attributes for the flip edge (we'll set ->next below) newHE->flip = he; newHE->vertex = nextHE->vertex; newHE->edge = he->edge; newHE->face = newBoundary; newHE->texcoord = nextHE->texcoord; // point the new face to this half edge newBoundary->he = newHE; // keep track of all the new halfedges in the boundary cycle boundaryCycle.push_back( newHE ); // continue to walk along the cycle he = nextHE; } while( he != currentHE ); // link together the cycle of boundary halfedges unsigned int N = boundaryCycle.size(); for( unsigned int i = 0; i < N; i++ ) { boundaryCycle[ i ]->next = boundaryCycle[ (i+N-1)%N ]; hasFlipEdge[ boundaryCycle[i] ] = true; hasFlipEdge[ boundaryCycle[i]->flip ] = true; } } } // print a warning if the input has any non-terminal defects checkIsolatedVertices( mesh ); checkNonManifoldVertices( mesh ); return 0; } void MeshIO :: readPosition( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.positions.push_back( Vector( x, y, z )); } void MeshIO :: readTexCoord( stringstream& ss, MeshData& data ) { double u, v; ss >> u >> v; data.texcoords.push_back( Vector( u, v, 0. )); } void MeshIO :: readNormal( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.normals.push_back( Vector( x, y, z )); } void MeshIO :: readFace( stringstream& ss, MeshData &data ) { vector faceIndices; string token; while( ss >> token ) { faceIndices.push_back( parseFaceIndex( token )); } data.indices.push_back( faceIndices ); } Index MeshIO :: parseFaceIndex( const string& token ) { // parse indices of the form // // p/[t]/[n] // // where p is an index into positions, t is an index into // texcoords, n is an index into normals, and [.] indicates // that an index is optional stringstream in( token ); string indexstring; int indices[3] = { -1, -1, -1 }; int i = 0; while( getline( in, indexstring, '/' )) { stringstream ss( indexstring ); ss >> indices[i++]; } // decrement since indices in OBJ files are 1-based return Index( indices[0]-1, indices[1]-1, indices[2]-1 ); } void MeshIO :: checkIsolatedVertices( const Mesh& mesh ) { // print a warning if the mesh has any isolated vertices int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { cerr << "Warning: vertex " << vertexIndex << " is isolated (not contained in any face)." << endl; } vertexIndex++; } } void MeshIO :: checkNonManifoldVertices( const Mesh& mesh ) { map nIncidentFaces; for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } for( FaceCIter f = mesh.boundaries.begin(); f != mesh.boundaries.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( nIncidentFaces[v] != v->valence() ) { cerr << "Warning: vertex " << vertexIndex << " is nonmanifold." << endl; } vertexIndex++; } } } ================================================ FILE: Elasticity/src/Quaternion.cpp ================================================ #include #include using namespace std; #include "Quaternion.h" namespace DDG { // CONSTRUCTORS ---------------------------------------------------------- Quaternion :: Quaternion( void ) // initializes all components to zero : s( 0. ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Quaternion& q ) // initializes from existing quaternion : s( q.s ), v( q.v ) {} Quaternion :: Quaternion( double s_, double vi, double vj, double vk ) // initializes with specified double (s) and imaginary (v) components : s( s_ ), v( vi, vj, vk ) {} Quaternion :: Quaternion( double s_, const Vector& v_ ) // initializes with specified double(s) and imaginary (v) components : s( s_ ), v( v_ ) {} Quaternion :: Quaternion( double s_ ) // initializes purely real quaternion with specified real (s) component (imaginary part is zero) : s( s_ ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Vector& v_ ) // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) : s( 0. ), v( v_ ) {} Quaternion :: Quaternion( const Complex& z ) // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k : s( z.re ), v( z.im, 0., 0. ) {} // ASSIGNMENT OPERATORS -------------------------------------------------- const Quaternion& Quaternion :: operator=( double _s ) // assigns a purely real quaternion with real value s { s = _s; v = Vector( 0., 0., 0. ); return *this; } const Quaternion& Quaternion :: operator=( const Vector& _v ) // assigns a purely real quaternion with imaginary value v { s = 0.; v = _v; return *this; } // ACCESSORS ------------------------------------------------------------- double& Quaternion::operator[]( int index ) // returns reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } const double& Quaternion::operator[]( int index ) const // returns const reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } void Quaternion::toMatrix( double Q[4][4] ) const // returns 4x4 matrix representation { Q[0][0] = s; Q[0][1] = -v.x; Q[0][2] = -v.y; Q[0][3] = -v.z; Q[1][0] = v.x; Q[1][1] = s; Q[1][2] = -v.z; Q[1][3] = v.y; Q[2][0] = v.y; Q[2][1] = v.z; Q[2][2] = s; Q[2][3] = -v.x; Q[3][0] = v.z; Q[3][1] = -v.y; Q[3][2] = v.x; Q[3][3] = s; } double& Quaternion::re( void ) // returns reference to double part { return s; } const double& Quaternion::re( void ) const // returns const reference to double part { return s; } Vector& Quaternion::im( void ) // returns reference to imaginary part { return v; } const Vector& Quaternion::im( void ) const // returns const reference to imaginary part { return v; } // VECTOR SPACE OPERATIONS ----------------------------------------------- Quaternion Quaternion::operator+( const Quaternion& q ) const // addition { return Quaternion( s+q.s, v+q.v ); } Quaternion Quaternion::operator-( const Quaternion& q ) const // subtraction { return Quaternion( s-q.s, v-q.v ); } Quaternion Quaternion::operator-( void ) const // negation { return Quaternion( -s, -v ); } Quaternion Quaternion::operator*( double c ) const // scalar multiplication { return Quaternion( s*c, v*c ); } Quaternion operator*( double c, const Quaternion& q ) // scalar multiplication { return q*c; } Quaternion Quaternion::operator/( double c ) const // scalar division { return Quaternion( s/c, v/c ); } void Quaternion::operator+=( const Quaternion& q ) // addition / assignment { s += q.s; v += q.v; } void Quaternion::operator+=( double c ) // addition / assignment of pure real { s += c; } void Quaternion::operator-=( const Quaternion& q ) // subtraction / assignment { s -= q.s; v -= q.v; } void Quaternion::operator-=( double c ) // subtraction / assignment of pure real { s -= c; } void Quaternion::operator*=( double c ) // scalar multiplication / assignment { s *= c; v *= c; } void Quaternion::operator/=( double c ) // scalar division / assignment { s /= c; v /= c; } // ALGEBRAIC OPERATIONS -------------------------------------------------- Quaternion Quaternion::operator*( const Quaternion& q ) const // Hamilton product { const double& s1( s ); const double& s2( q.s ); const Vector& v1( v ); const Vector& v2( q.v ); return Quaternion( s1*s2 - dot(v1,v2), s1*v2 + s2*v1 + cross(v1,v2) ); } void Quaternion::operator*=( const Quaternion& q ) // Hamilton product / assignment { *this = ( *this * q ); } Quaternion Quaternion::conj( void ) const // conjugation { return Quaternion( s, -v ); } Quaternion Quaternion::inv( void ) const { return ( this->conj() ) / this->norm2(); } // NORMS ----------------------------------------------------------------- double Quaternion::norm( void ) const // returns Euclidean length { return sqrt( s*s + v.x*v.x + v.y*v.y + v.z*v.z ); } double Quaternion::norm2( void ) const // returns Euclidean length squared { return s*s + dot(v,v); } Quaternion Quaternion::unit( void ) const // returns unit quaternion { return *this / norm(); } void Quaternion::normalize( void ) // divides by Euclidean length { *this /= norm(); } // GEOMETRIC OPERATIONS -------------------------------------------------- Quaternion slerp( const Quaternion& q0, const Quaternion& q1, double t ) // spherical-linear interpolation { // interpolate length double m0 = q0.norm(); double m1 = q1.norm(); double m = (1-t)*m0 + t*m1; // interpolate direction Quaternion p0 = q0 / m0; Quaternion p1 = q1 / m1; double theta = acos(( p0.conj()*p1 ).re() ); Quaternion p = ( sin((1-t)*theta)*p0 + sin(t*theta)*p1 )/sin(theta); return m*p; } // I/O ------------------------------------------------------------------------- std::ostream& operator<<( std::ostream& os, const Quaternion& q ) // prints components { os << "( " << q.re() << ", " << q.im() << " )"; return os; } } ================================================ FILE: Elasticity/src/Real.cpp ================================================ #include "Real.h" #include namespace DDG { Real :: Real( double x ) // constructs real number with value x : value( x ) {} Real :: operator double( void ) const // type cast to double { return value; } void Real :: operator+=( double x ) // increment { value += x; } void Real :: operator-=( double x ) // decrement { value -= x; } void Real :: operator*=( double x ) // multiply { value *= x; } void Real :: operator/=( double x ) // divide { value /= x; } Real Real :: conj( void ) const // simply returns the value (for compatibility w/ complex numbers) { return value; } Real Real :: inv( void ) const // returns inverse { return 1. / value; } double Real :: norm( void ) const // returns norm { return fabs( value ); } double Real :: norm2( void ) const // returns norm squared { return value * value; } Real Real :: unit( void ) const // returns number with unit norm and same sign { return value / norm(); } } ================================================ FILE: Elasticity/src/Shader.cpp ================================================ #include "Shader.h" #include #include using namespace std; namespace DDG { Shader::Shader( void ) // constructor -- shader is initially invalid : vertexShader( 0 ), fragmentShader( 0 ), geometryShader( 0 ), program( 0 ), linked( false ) {} Shader::~Shader( void ) { if( program ) glDeleteProgram( program ); if( vertexShader ) glDeleteShader( vertexShader ); if( fragmentShader ) glDeleteShader( fragmentShader ); if( geometryShader ) glDeleteShader( geometryShader ); } void Shader::loadVertex( const char* filename ) { load( GL_VERTEX_SHADER, filename, vertexShader ); } void Shader::loadFragment( const char* filename ) { load( GL_FRAGMENT_SHADER, filename, fragmentShader ); } void Shader::loadGeometry( const char* filename ) { #ifdef GL_GEOMETRY_SHADER_EXT load( GL_GEOMETRY_SHADER_EXT, filename, geometryShader ); #else cerr << "Error: geometry shaders not supported!" << endl; #endif } void Shader::enable( void ) { if( !linked ) { glLinkProgram( program ); linked = true; } glUseProgram( program ); } void Shader::disable( void ) const { glUseProgram( 0 ); } Shader::operator GLuint( void ) const { return program; } void Shader::load( GLenum shaderType, const char* filename, GLuint& shader ) // read vertex shader from GLSL source file, compile, and attach to program { string source; if( !readSource( filename, source )) { return; } if( program == 0 ) { program = glCreateProgram(); } if( shader != 0 ) { glDetachShader( program, shader ); } shader = glCreateShader( shaderType ); const char* source_c_str = source.c_str(); glShaderSource( shader, 1, &(source_c_str), NULL ); glCompileShader( shader ); GLint compileStatus; glGetShaderiv( shader, GL_COMPILE_STATUS, &compileStatus ); if( compileStatus == GL_TRUE ) { glAttachShader( program, shader ); linked = false; } else { GLsizei maxLength = 0; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength ); if( maxLength > 0 ) { GLchar* infoLog = new char[ maxLength ]; GLsizei length; glGetShaderInfoLog( shader, maxLength, &length, infoLog ); cerr << "GLSL Error: " << infoLog << endl; delete[] infoLog; } } } bool Shader::readSource( const char* filename, std::string& source ) // reads GLSL source file into a string { source = ""; ifstream in( filename ); if( !in.is_open() ) { cerr << "Error: could not open shader file "; cerr << filename; cerr << " for input!" << endl; return false; } string line; while( getline( in, line )) { source += line; } return true; } } ================================================ FILE: Elasticity/src/SparseMatrix.cpp ================================================ #include "SparseMatrix.h" namespace DDG { template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = pr[k]; } } return *this; } template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = Complex( pr[k*2+0], pr[k*2+1] ); } } return *this; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ) { SparseMatrix A( m*4, n*4 ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; const Quaternion& q( e->second ); A(i*4+0,j*4+0) = q[0]; A(i*4+0,j*4+1) = -q[1]; A(i*4+0,j*4+2) = -q[2]; A(i*4+0,j*4+3) = -q[3]; A(i*4+1,j*4+0) = q[1]; A(i*4+1,j*4+1) = q[0]; A(i*4+1,j*4+2) = -q[3]; A(i*4+1,j*4+3) = q[2]; A(i*4+2,j*4+0) = q[2]; A(i*4+2,j*4+1) = q[3]; A(i*4+2,j*4+2) = q[0]; A(i*4+2,j*4+3) = -q[1]; A(i*4+3,j*4+0) = q[3]; A(i*4+3,j*4+1) = -q[2]; A(i*4+3,j*4+2) = q[1]; A(i*4+3,j*4+3) = q[0]; } if( cData != NULL ) { cholmod_l_free_sparse( &cData, context ); } cData = cholmod_l_copy_sparse( A.to_cholmod(), context ); return cData; } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_COMPLEX, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m*4, n*4, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i] = e->second; } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i*2+0] = e->second.re; pr[i*2+1] = e->second.im; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR< complex >( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (complex)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (quaternion)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4]/4 << "\n"; } template <> void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_zl_symbolic( n, n, Ap, Ai, Ax, NULL, &Symbolic, NULL, NULL ); umfpack_zl_numeric( Ap, Ai, Ax, NULL, Symbolic, &Numeric, NULL, NULL ); umfpack_zl_solve( UMFPACK_A, Ap, Ai, Ax, NULL, (double*) &x(0), NULL, (double*) &b(0), NULL, Numeric, NULL, NULL ); umfpack_zl_free_symbolic( &Symbolic ); umfpack_zl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } } ================================================ FILE: Elasticity/src/SparseMatrix.inl ================================================ #include #include #include #include #include using namespace std; #include #include #include "Real.h" #include "Complex.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "LinearContext.h" #include "Utility.h" namespace DDG { extern LinearContext context; const int maxEigIter = 20; // number of iterations used to solve eigenvalue problems template SparseMatrix :: SparseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) {} template SparseMatrix :: SparseMatrix( const SparseMatrix& B ) // copy constructor : cData( NULL ) { *this = B; } template SparseMatrix :: ~SparseMatrix( void ) // destructor { if( cData ) { cholmod_l_free_sparse( &cData, context ); } } template const SparseMatrix& SparseMatrix :: operator=( const SparseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template SparseMatrix SparseMatrix :: transpose( void ) const { SparseMatrix AT( n, m ); for( const_iterator e = begin(); e != end(); e++ ) { int i = e->first.second; int j = e->first.first; T Aij = e->second; AT(j,i) = Aij.conj(); } return AT; } template SparseMatrix SparseMatrix :: operator*( const SparseMatrix& B ) const // returns product of this matrix with sparse B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // collect nonzeros in each row vector< vector< int > > Bcol( B.nRows() ); vector< vector< T > > Bval( B.nRows() ); for( const_iterator e = B.begin(); e != B.end(); e ++ ) { int row = e->first.second; int col = e->first.first; T val = e->second; Bcol[ row ].push_back( col ); Bval[ row ].push_back( val ); } // multiply C = A*B SparseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( size_t n = 0; n < Bcol[j].size(); n++ ) { int k = Bcol[j][n]; C( i, k ) += e->second * Bval[j][n]; } } return C; } template DenseMatrix SparseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with dense B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // multiply C = A*B DenseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( int k = 0; k < B.nColumns(); k++ ) { C( i, k ) += e->second * B( j, k ); } } return C; } template void SparseMatrix :: operator*=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second *= c; } } template void SparseMatrix :: operator/=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second /= c; } } template void SparseMatrix :: operator+=( const SparseMatrix& B ) // adds B to this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) += Bij; } } template void SparseMatrix :: operator-=( const SparseMatrix& B ) // subtracts B from this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) -= Bij; } } template SparseMatrix SparseMatrix :: operator+( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C += B; return C; } template SparseMatrix SparseMatrix :: operator-( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C -= B; return C; } template SparseMatrix operator*( const T& c, const SparseMatrix& A ) { SparseMatrix cA = A; for( typename SparseMatrix::iterator e = cA.begin(); e != cA.end(); e++ ) { e->second = c * e->second; } return cA; } template SparseMatrix operator*( const SparseMatrix& A, const T& c ) { SparseMatrix Ac = A; Ac *= c; return Ac; } template SparseMatrix operator/( const SparseMatrix& A, T c ) { SparseMatrix Ac = A; Ac /= c; return Ac; } template void SparseMatrix :: resize( int m_, int n_ ) { m = m_; n = n_; data.clear(); } template int SparseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int SparseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int SparseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void SparseMatrix :: zero( const T& val ) // sets all nonzero elements val { for( iterator i = begin(); i != end(); i++ ) { i->second = val; } } template SparseMatrix SparseMatrix :: inverse( void ) const // returns inverse -- for diagonal matrices only { assert( m == n ); // matrix must be square const SparseMatrix& A( *this ); SparseMatrix Ainv( m, m ); for( const_iterator e = begin(); e != end(); e++ ) { int r = e->first.second; int c = e->first.first; assert( r == c ); // matrix must be diagonal Ainv( r, c ) = A( r, c ).inv(); } return Ainv; } template SparseMatrix SparseMatrix :: identity( int N ) { SparseMatrix I( N, N ); for( int i = 0; i < N; i++ ) { I( i, i ) = 1.; } return I; } template DenseMatrix SparseMatrix :: full( void ) const // converts to a dense matrix { const int maxSize = 1048576; if( m*n > maxSize ) { cerr << "Error: refusing to convert sparse to dense (too big!)" << "\n"; exit( 1 ); } const SparseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < m; j++ ) { B( i, j ) = A( i, j ); } return B; } template cholmod_sparse* SparseMatrix :: to_cholmod( void ) { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } allocateSparse(); // build compressed matrix (note that EntryMap stores entries in column-major order) double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; int i = 0; int j = -1; for( const_iterator e = begin(); e != end(); e ++ ) { int c = e->first.first; if( c != j ) { for( int k = j+1; k <= c; k++ ) { jc[k] = i; } j = c; } ir[i] = e->first.second; setEntry( e, i, pr ); i++; } for( int k = j+1; k <= n; k++ ) { jc[k] = i; } return cData; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ); template T& SparseMatrix :: operator()( int row, int col ) { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { data[ index ] = T( 0. ); } return data[ index ]; } template T SparseMatrix :: operator()( int row, int col ) const { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { return T( 0. ); } return entry->second; } template typename SparseMatrix::iterator SparseMatrix :: begin( void ) { return data.begin(); } template typename SparseMatrix::const_iterator SparseMatrix :: begin( void ) const { return data.begin(); } template typename SparseMatrix::iterator SparseMatrix :: end( void ) { return data.end(); } template typename SparseMatrix::const_iterator SparseMatrix :: end( void ) const { return data.end(); } template void SparseMatrix :: shift( double c ) // adds c times the identity matrix to this matrix { assert( m == n ); SparseMatrix& A( *this ); for( int i = 0; i < m; i++ ) { A( i, i ) += c; } } template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_dl_symbolic( n, n, Ap, Ai, Ax, &Symbolic, NULL, NULL ); umfpack_dl_numeric( Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL ); umfpack_dl_solve( UMFPACK_A, Ap, Ai, Ax, (double*) &x(0), (double*) &b(0), Numeric, NULL, NULL ); umfpack_dl_free_symbolic( &Symbolic ); umfpack_dl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; cholmod_factor* L = cholmod_l_analyze( Ac, context ); cholmod_l_factorize( Ac, L, context ); x = cholmod_l_solve( CHOLMOD_A, L, b.to_cholmod(), context ); if( L ) cholmod_l_free_factor( &L, context ); int t1 = clock(); cout << "[chol] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[chol] max residual: " << residual( A, x, b ) << "\n"; } template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ) // backsolves the prefactored positive definite sparse linear system LL'x = b { x = cholmod_l_solve( CHOLMOD_A, L.to_cholmod(), b.to_cholmod(), context ); } template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess { int t0 = clock(); for( int iter = 0; iter < maxEigIter; iter++ ) { solve( A, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess { // TODO use a symmetric matrix decomposition instead of QR int t0 = clock(); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= dot( e, B*e ).norm(); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; solve( A, x, x ); x -= dot( x, Be ).conj()*e; x /= dot( x, B*x ).norm(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); for( int iter = 0; iter < maxEigIter; iter++ ) { backsolvePositiveDefinite( L, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= sqrt( dot( e, B*e ).norm() ); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; backsolvePositiveDefinite( L, x, x ); x -= dot( x, Be ).conj()*e; x /= sqrt( dot( x, B*x ).norm() ); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ) // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess { int iter; int t0 = clock(); DenseMatrix ET = E.transpose(); SparseFactor L; L.build( A ); for( iter = 0; iter < maxEigIter; iter++ ) { x = B*x - E*(ET*x); backsolvePositiveDefinite( L, x, x ); x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, E, x ) << "\n"; } template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ) // returns the max residual of the linear problem A x = b relative to the largest entry of the solution { return ( A*x - b ).norm() / b.norm(); } template double residual( const SparseMatrix& A, const DenseMatrix& x ) // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, x ); return (A*x-lambda*x).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, x ); return (A*x-lambda*(B*x)).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, E, x ); return (A*x-lambda*(B*x-E*(E.transpose()*x))).norm() / x.norm(); } template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*x)(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x))(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns /<(B-EE^T)x,x> { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x-E*(E.transpose()*x)))(0).inv(); } template std::ostream& operator<<( std::ostream& os, const SparseMatrix& o) { os.precision( 3 ); for( typename SparseMatrix::const_iterator e = o.begin(); e != o.end(); e ++ ) { int row = e->first.second; int col = e->first.first; os << "( " << row << ", " << col << " ): " << e->second << "\n"; } return os; } template SparseFactor :: SparseFactor( void ) : L( NULL ) {} template SparseFactor :: ~SparseFactor( void ) { if( L ) { cholmod_l_free_factor( &L, context ); } } template void SparseFactor :: build( SparseMatrix& A ) { if( L ) { cholmod_l_free_factor( &L, context ); L = NULL; } int t0, t1; cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; t0 = clock(); L = cholmod_l_analyze( Ac, context ); t1 = clock(); cerr << "analyze: " << seconds(t0,t1) << "s" << endl; t0 = clock(); cholmod_l_factorize( Ac, L, context ); t1 = clock(); cerr << "factorize: " << seconds(t0,t1) << "s" << endl; } template bool SparseFactor :: valid( void ) const { if( L == NULL ) { return false; } return true; } template cholmod_factor* SparseFactor :: to_cholmod( void ) { return L; } } ================================================ FILE: Elasticity/src/Variable.cpp ================================================ #include "Variable.h" namespace DDG { Variable :: Variable( double value_, bool fixed_ ) // initialize a variable which has value zero and is not fixed by default : value( value_ ), fixed( fixed_ ) {} Variable :: Variable( std::string name_, double value_, bool fixed_ ) // initialize a named variable which has value zero and is not fixed by default : name( name_ ), value( value_ ), fixed( fixed_ ) {} double& Variable :: operator*( void ) // returns a reference to the numerical value { return value; } const double& Variable :: operator*( void ) const // returns a const reference to the numerical value { return value; } } ================================================ FILE: Elasticity/src/Vector.cpp ================================================ #include #include "Vector.h" namespace DDG { Vector :: Vector( void ) : x( 0. ), y( 0. ), z( 0. ) {} Vector :: Vector( double x0, double y0, double z0 ) : x( x0 ), y( y0 ), z( z0 ) {} Vector :: Vector( const Vector& v ) : x( v.x ), y( v.y ), z( v.z ) {} double& Vector :: operator[]( const int& index ) { return ( &x )[ index ]; } const double& Vector :: operator[]( const int& index ) const { return ( &x )[ index ]; } Vector Vector :: operator+( const Vector& v ) const { return Vector( x + v.x, y + v.y, z + v.z ); } Vector Vector :: operator-( const Vector& v ) const { return Vector( x - v.x, y - v.y, z - v.z ); } Vector Vector :: operator-( void ) const { return Vector( -x, -y, -z ); } Vector Vector :: operator*( const double& c ) const { return Vector( x*c, y*c, z*c ); } Vector operator*( const double& c, const Vector& v ) { return v*c; } Vector Vector :: operator/( const double& c ) const { return (*this) * ( 1./c ); } void Vector :: operator+=( const Vector& v ) { x += v.x; y += v.y; z += v.z; } void Vector :: operator-=( const Vector& v ) { x -= v.x; y -= v.y; z -= v.z; } void Vector :: operator*=( const double& c ) { x *= c; y *= c; z *= c; } void Vector :: operator/=( const double& c ) { (*this) *= ( 1./c ); } double Vector :: norm( void ) const { return sqrt( norm2()); } double Vector :: norm2( void ) const { return dot( *this, *this ); } void Vector :: normalize( void ) { (*this) /= norm(); } Vector Vector :: unit( void ) const { return (*this) / norm(); } Vector Vector :: abs( void ) const { return Vector( fabs( x ), fabs( y ), fabs( z ) ); } double dot( const Vector& u, const Vector& v ) { return u.x*v.x + u.y*v.y + u.z*v.z ; } Vector cross( const Vector& u, const Vector& v ) { return Vector( u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x ); } std::ostream& operator << (std::ostream& os, const Vector& o) { os << "[ " << o.x << " " << o.y << " " << o.z << " ]"; return os; } } ================================================ FILE: Elasticity/src/Vertex.cpp ================================================ #include using namespace std; #include "Vertex.h" #include "Mesh.h" #include "HalfEdge.h" namespace DDG { double Vertex::area( void ) const // returns the dual area associated with this vertex { double A = 0.; HalfEdgeCIter h = he; do { if (not h->onBoundary) A += h->face->area(); h = h->flip->next; } while( h != he ); return A / 3.; } Vector Vertex::normal( void ) const // returns the vertex normal { Vector N; HalfEdgeCIter h = he; do { if (not h->onBoundary) N += h->face->normal(); h = h->flip->next; } while( h != he ); return N.unit(); } vector isolated; // all isolated vertices point to isolated.begin() bool Vertex::isIsolated( void ) const // returns true if the vertex is not contained in any face or edge; false otherwise { return he == isolated.begin(); } int Vertex :: valence( void ) const // returns the number of incident faces { int n = 0; HalfEdgeCIter h = he; do { n++; h = h->flip->next; } while( h != he ); return n; } void Vertex :: toggleTag() { tag = !tag; } } ================================================ FILE: Elasticity/src/Viewer.cpp ================================================ #include #include #include #include #include #include #include using namespace std; #include "Viewer.h" #include "Image.h" #include "Application.h" namespace DDG { // declare static member variables Mesh Viewer::mesh; Mesh Viewer::source; Mesh Viewer::target; Mesh Viewer::initial; GLuint Viewer::surfaceDL = 0; int Viewer::windowSize[2] = { 512, 512 }; Camera Viewer::camera; Shader Viewer::shader; bool Viewer::renderWireframe = false; double Viewer::step = 0.0; double Viewer::delta = 0.1; bool Viewer::renderInitial = true; bool Viewer::animate = false; void Viewer :: init( void ) { restoreViewerState(); initGLUT(); setGL(); initGLSL(); updateDisplayList(); glutMainLoop(); } void Viewer :: initGLUT( void ) { int argc = 0; vector< vector > argv(1); // initialize window glutInitWindowSize( windowSize[0], windowSize[1] ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); glutInit( &argc, (char**)&argv ); glutCreateWindow( "DDG" ); // specify callbacks glutDisplayFunc ( Viewer::display ); glutIdleFunc ( Viewer::idle ); glutKeyboardFunc ( Viewer::keyboard ); glutSpecialFunc ( Viewer::special ); glutMouseFunc ( Viewer::mouse ); glutMotionFunc ( Viewer::motion ); // initialize menus int viewMenu = glutCreateMenu( Viewer::view ); glutSetMenu( viewMenu ); glutAddMenuEntry( "[f] Wireframe", menuWireframe ); glutAddMenuEntry( "[↑] Zoom In", menuZoomIn ); glutAddMenuEntry( "[↓] Zoom Out", menuZoomOut ); glutAddMenuEntry( "[i] Initial", menuRenderInitial ); glutAddMenuEntry( "[-] DecreaseStep", menuDecreaseStep ); glutAddMenuEntry( "[=] IncreaseStep", menuIncreaseStep ); int mainMenu = glutCreateMenu( Viewer::menu ); glutSetMenu( mainMenu ); glutAddMenuEntry( "[space] Process Mesh", menuProcess ); glutAddMenuEntry( "[r] Reset Mesh", menuResetMesh ); glutAddMenuEntry( "[w] Write Mesh", menuWriteMesh ); glutAddMenuEntry( "[\\] Screenshot", menuScreenshot ); glutAddMenuEntry( "[esc] Exit", menuExit ); glutAddSubMenu( "View", viewMenu ); glutAttachMenu( GLUT_RIGHT_BUTTON ); } void Viewer :: initGLSL( void ) { shader.loadVertex( "shaders/vertex.glsl" ); shader.loadFragment( "shaders/fragment.glsl" ); } void Viewer :: menu( int value ) { switch( value ) { case( menuProcess ): mProcess(); break; case( menuResetMesh ): mResetMesh(); break; case( menuWriteMesh ): mWriteMesh(); break; case( menuScreenshot ): mScreenshot(); break; case( menuExit ): mExit(); break; case( menuIncreaseStep ): mIncreaseStep(); break; case( menuDecreaseStep ): mDecreaseStep(); break; default: break; } } void Viewer :: view( int value ) { switch( value ) { case( menuWireframe ): mWireframe(); break; case( menuZoomIn ): mZoomIn(); break; case( menuZoomOut ): mZoomOut(); break; case( menuRenderInitial ): mRenderInitial(); break; default: break; } } void Viewer :: keyboard( unsigned char c, int x, int y ) { switch( c ) { case 'f': mWireframe(); break; case 'w': mWriteMesh(); break; case 'r': mResetMesh(); break; case '\\': mScreenshot(); break; case ' ': animate = !animate; break; case 27: mExit(); break; case '-': mDecreaseStep(); break; case '=': mIncreaseStep(); break; case 'i': mRenderInitial(); break; default: break; } } void Viewer :: special( int i, int x, int y ) { switch( i ) { case GLUT_KEY_UP: camera.zoomIn(); break; case GLUT_KEY_DOWN: camera.zoomOut(); break; case 27: mExit(); break; default: break; } } void Viewer :: mouse( int button, int state, int x, int y ) { camera.mouse( button, state, x, y ); } void Viewer :: motion( int x, int y ) { camera.motion( x, y ); } void Viewer :: idle( void ) { if( animate ) { mProcess(); } camera.idle(); glutPostRedisplay(); } void Viewer :: storeViewerState( void ) { ofstream out( ".viewer_state.txt" ); out << camera.rLast[0] << endl; out << camera.rLast[1] << endl; out << camera.rLast[2] << endl; out << camera.rLast[3] << endl; GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); out << view[2] << endl; out << view[3] << endl; } void Viewer :: restoreViewerState( void ) { ifstream in( ".viewer_state.txt" ); if( !in.is_open() ) return; in >> camera.rLast[0]; in >> camera.rLast[1]; in >> camera.rLast[2]; in >> camera.rLast[3]; in >> windowSize[0]; in >> windowSize[1]; } void Viewer :: mRenderInitial( void ) { renderInitial = !renderInitial; updateDisplayList(); } void Viewer :: mIncreaseStep( void ) { step += delta; step = std::min(step, 1.0); Application app; app.init(step, source, target, mesh); app.init(step, source, target, initial); updateDisplayList(); std::cout << "Step = " << step << std::endl; } void Viewer :: mDecreaseStep( void ) { step -= delta; step = std::max(step, 0.0); Application app; app.init(step, source, target, mesh); app.init(step, source, target, initial); updateDisplayList(); std::cout << "Step = " << step << std::endl; } void Viewer :: mProcess( void ) { Application app; app.interpolate(step, source, target, mesh, 1); updateDisplayList(); } void Viewer :: mResetMesh( void ) { mesh.reload(); updateDisplayList(); } void Viewer :: mWriteMesh( void ) { mesh.write( "out.obj" ); } void Viewer :: mExit( void ) { //storeViewerState(); exit( 0 ); } void Viewer :: mWireframe( void ) { renderWireframe = !renderWireframe; updateDisplayList(); } void Viewer :: mZoomIn( void ) { camera.zoomIn(); } void Viewer :: mZoomOut( void ) { camera.zoomOut(); } void Viewer :: mScreenshot( void ) { static int index = 0; // get window width and height GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); int w = view[2]; int h = view[3]; // get pixels Image image( w, h ); glReadPixels( 0, 0, w, h, GL_BGR, GL_FLOAT, &image(0,0) ); stringstream filename; filename << "frames/viewer" << setw(8) << setfill( '0' ) << index << ".tga"; image.write( filename.str().c_str() ); index++; } void Viewer :: display( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); shader.enable(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); double aspect = (double) viewport[2] / (double) viewport[3]; const double fovy = 50.; const double clipNear = .01; const double clipFar = 1000.; gluPerspective( fovy, aspect, clipNear, clipFar ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); Quaternion eye = Vector( 0., 0., -2.5*camera.zoom ); Quaternion center = Vector( 0., 0., 0. ); Quaternion up = Vector( 0., 1., 0. ); gluLookAt( eye[1], eye[2], eye[3], center[1], center[2], center[3], up[1], up[2], up[3] ); Quaternion r = camera.currentRotation(); eye = r.conj() * eye * r; GLint uniformEye = glGetUniformLocation( shader, "eye" ); glUniform3f( uniformEye, eye[1], eye[2], eye[3] ); Quaternion light = Vector( -1., 1., -2. ); light = r.conj() * light * r; GLint uniformLight = glGetUniformLocation( shader, "light" ); glUniform3f( uniformLight, light[1], light[2], light[3] ); camera.setView(); callDisplayList(); shader.disable(); glutSwapBuffers(); } void Viewer :: updateDisplayList( void ) { if( surfaceDL ) { glDeleteLists( surfaceDL, 1 ); surfaceDL = 0; } surfaceDL = glGenLists( 1 ); glNewList( surfaceDL, GL_COMPILE ); setMeshMaterial(); drawScene(); glEndList(); } void Viewer :: setGL( void ) { glClearColor( .5, .5, .5, 1. ); setLighting(); } void Viewer :: setLighting( void ) { GLfloat position[4] = { 20., 30., 40., 0. }; glLightfv( GL_LIGHT0, GL_POSITION, position ); glEnable( GL_LIGHT0 ); glEnable( GL_NORMALIZE ); } void Viewer :: setMeshMaterial( void ) { GLfloat diffuse[4] = { .8, .5, .3, 1. }; GLfloat specular[4] = { .3, .3, .3, 1. }; GLfloat ambient[4] = { .2, .2, .5, 1. }; glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular ); glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient ); glMaterialf ( GL_FRONT_AND_BACK, GL_SHININESS, 16. ); } void Viewer :: callDisplayList( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_DEPTH_TEST ); glEnable( GL_LIGHTING ); glCallList( surfaceDL ); glPopAttrib(); } void Viewer :: drawScene( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable(GL_COLOR_MATERIAL); if( renderInitial ) { glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1., 1. ); glColor3f( 0.5, 0.5, 0. ); drawPolygons( initial ); glDisable( GL_POLYGON_OFFSET_FILL ); if( renderWireframe ) drawWireframe( initial ); } glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 5., 1. ); glColor3f( .8, .5, .3 ); drawPolygons( mesh ); glDisable( GL_POLYGON_OFFSET_FILL ); if( renderWireframe ) drawWireframe( mesh ); glPopAttrib(); } void Viewer :: drawPolygons( const Mesh& surf ) { for( FaceCIter f = surf.faces.begin(); f != surf.faces.end(); f ++ ) { if( f->isBoundary() ) continue; glBegin( GL_POLYGON ); if( renderWireframe ) { Vector N = f->normal(); glNormal3dv( &N[0] ); } HalfEdgeCIter he = f->he; do { if( not renderWireframe ) { Vector N = he->vertex->normal(); glNormal3dv( &N[0] ); } glVertex3dv( &he->vertex->position[0] ); he = he->next; } while( he != f->he ); glEnd(); } } void Viewer :: drawWireframe( const Mesh& surf ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glDisable( GL_LIGHTING ); glColor4f( 0., 0., 0., 0.5 ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBegin( GL_LINES ); for( EdgeCIter e = surf.edges.begin(); e != surf.edges.end(); e ++ ) { glVertex3dv( &e->he->vertex->position[0] ); glVertex3dv( &e->he->flip->vertex->position[0] ); } glEnd(); glPopAttrib(); } } ================================================ FILE: Elasticity/src/main.cpp ================================================ #include using namespace std; #include "Viewer.h" #include "DenseMatrix.h" using namespace DDG; int main( int argc, char** argv ) { if( argc != 3 ) { cerr << "usage: " << argv[0] << " src.obj tgt.obj" << endl; return 1; } Viewer viewer; viewer.source.read( argv[1] ); viewer.target.read( argv[2] ); viewer.initial.read( argv[1] ); viewer.mesh.read( argv[1] ); viewer.init(); return 0; } ================================================ FILE: Fairing/.viewer_state.txt ================================================ 0.126663 -0.0243652 -0.989174 0.0699843 815 767 ================================================ FILE: Fairing/Makefile ================================================ ########################################################################################## # Specify library locations here (add or remove "#" marks to comment/uncomment lines for your platform) # Mac OS X DDG_INCLUDE_PATH = DDG_LIBRARY_PATH = DDG_BLAS_LIBS = -framework Accelerate DDG_SUITESPARSE_LIBS = -lspqr -lumfpack -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -ltbb -lm -lsuitesparseconfig DDG_OPENGL_LIBS = -framework OpenGL -framework GLUT # # Linux # DDG_INCLUDE_PATH = # DDG_LIBRARY_PATH = # DDG_BLAS_LIBS = -llapack -lblas -lgfortran # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut -lGL -lGLU -lX11 # # Windows / Cygwin # DDG_INCLUDE_PATH = -I/usr/include/opengl -I/usr/include/suitesparse # DDG_LIBRARY_PATH = -L/usr/lib/w32api -L/usr/lib/suitesparse # DDG_BLAS_LIBS = -llapack -lblas # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut32 -lglu32 -lopengl32 ######################################################################################## TARGET = fairing CC = g++ LD = g++ CFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_INCLUDE_PATH) -I./include -I./src LFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_LIBRARY_PATH) LIBS = $(DDG_OPENGL_LIBS) $(DDG_SUITESPARSE_LIBS) $(DDG_BLAS_LIBS) ######################################################################################## ## !! Do not edit below this line HEADERS := $(wildcard include/*.h) SOURCES := $(wildcard src/*.cpp) OBJECTS := $(addprefix obj/,$(notdir $(SOURCES:.cpp=.o))) all: $(TARGET) $(TARGET): $(OBJECTS) $(LD) $(OBJECTS) -o $(TARGET) $(CFLAGS) $(LFLAGS) $(LIBS) obj/%.o: src/%.cpp ${HEADERS} $(CC) -c $< -o $@ $(CFLAGS) clean: rm -f $(OBJECTS) rm -f $(TARGET) rm -f $(TARGET).exe ================================================ FILE: Fairing/include/Application.h ================================================ /* * Implicit Fairing of Arbitrary Meshes using Diffusion and Curvature Flow * Mathieu Desbrun, Mark Meyer, Peter Schröder, Alan H. Barr * ACM Siggraph '99 Proceedings */ #ifndef DDG_APPLICATION_H #define DDG_APPLICATION_H #include "Mesh.h" #include "Real.h" #include "DenseMatrix.h" #include "SparseMatrix.h" #include "DiscreteExteriorCalculus.h" namespace DDG { class Application { public: void run(const double step, Mesh& mesh) { SparseMatrix star0; HodgeStar0Form::build( mesh, star0 ); SparseMatrix star1; HodgeStar1Form::build( mesh, star1 ); SparseMatrix d0; ExteriorDerivative0Form::build( mesh, d0 ); SparseMatrix L = d0.transpose() * star1 * d0; SparseMatrix A = star0 + Real(step) * L; DenseMatrix x; getPositions(mesh, x); DenseMatrix rhs = star0 * x; solvePositiveDefinite(A, x, rhs); setPositions(x, mesh); } protected: void getPositions(const Mesh& mesh, DenseMatrix& x) const { x = DenseMatrix( mesh.vertices.size(), 3 ); for ( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++) { for( int i = 0; i < 3; ++i) x(v->index, i) = v->position[i]; } } void setPositions(const DenseMatrix& x, Mesh& mesh) { for ( VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++) { v->position = Vector(x(v->index, 0), x(v->index, 1), x(v->index, 2)); } } }; } #endif ================================================ FILE: Fairing/include/Camera.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Camera.h // ----------------------------------------------------------------------------- // // Camera is used by Viewer to keep track of the view state; it also // handles mouse input related to camera manipulation. // #ifndef DDG_CAMERA_H #define DDG_CAMERA_H #include "Quaternion.h" #ifdef __CYGWIN__ #define GLUT_DISABLE_ATEXIT_HACK #include #include #include #include #else #include #endif namespace DDG { class Camera { public: Camera( void ); // constructor Quaternion clickToSphere( int x, int y ); // projects a mous click onto the unit sphere void setView( void ) const; // applies the camera transformation to the OpenGL modelview stack void mouse( int button, int state, int x, int y ); // handles mouse clicks void motion( int x, int y ); // handles mouse drags void idle( void ); // handles camera momentum void zoomIn( void ); // moves viewer toward object void zoomOut( void ); // moves viewer away from object Quaternion currentRotation( void ) const; // returns the rotation corresponding to the current mouse state Quaternion pClick; // mouse coordinates of current click Quaternion pDrag; // mouse coordinates of current drag Quaternion pLast; // mouse coordinates of previous drag Quaternion rLast; // previous camera rotation Quaternion momentum; // camera momentum int tLast; // time of previous drag double zoom, vZoom; // zoom and zoom velocity }; } #endif ================================================ FILE: Fairing/include/Complex.h ================================================ #ifndef DDG_COMPLEX_H #define DDG_COMPLEX_H #include namespace DDG { class Complex { public: Complex( double a=0., double b=0. ); // constructs number a+bi void operator+=( const Complex& z ); // add z void operator-=( const Complex& z ); // subtract z void operator*=( const Complex& z ); // Complex multiply by z void operator*=( double r ); // scalar multiply by r void operator/=( double r ); // scalar divide by r void operator/=( const Complex& z ); // complex divide by r Complex operator-( void ) const; // returns the additive inverse Complex conj( void ) const; // returns Complex conjugate Complex inv( void ) const; // returns inverse double arg( void ) const; // returns argument double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Complex unit( void ) const; // returns complex number with unit norm and same modulus Complex exponential( void ) const; // complex exponentiation double re; // real part double im; // imaginary part }; Complex operator+( const Complex& z1, const Complex& z2 ); // binary addition Complex operator-( const Complex& z1, const Complex& z2 ); // binary subtraction Complex operator*( const Complex& z1, const Complex& z2 ); // binary Complex multiplication Complex operator*( const Complex& z, double r ); // right scalar multiplication Complex operator*( double r, const Complex& z ); // left scalar multiplication Complex operator/( const Complex& z, double r ); // scalar division Complex operator/( const Complex& z1, const Complex& z2 ); // complex division double dot( const Complex& z1, const Complex& z2 ); // inner product double cross( const Complex& z1, const Complex& z2 ); // cross product std::ostream& operator<<( std::ostream& os, const Complex& o ); // prints components } #endif ================================================ FILE: Fairing/include/DenseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DenseMatrix.h // ----------------------------------------------------------------------------- // // DenseMatrix represents an m by n (real or complex) matrix where every // entry -- including zero-valued entries -- is stored explicitly. This // class is most commonly used to represent dense vectors in sparse linear // systems (i.e., the right hand side and the solution vector). // // A real or complex matrix is allocated via // // DenseMatrix A( m, n ); // DenseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // DenseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a DenseMatrix returns a cholmod_dense* // which can be used by routines in SuiteSparse. For basic operations, however, // you should not need to access this pointer explicitly -- see the solve() // method in SparseMatrix.h. // #ifndef DDG_DENSEMATRIX_H #define DDG_DENSEMATRIX_H #include #include "Types.h" #include namespace DDG { enum NormType { lInfinity, lOne, lTwo }; template class DenseMatrix { public: DenseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix (specifying just m yields a column vector) DenseMatrix( const DenseMatrix& A ); // copy constructor const DenseMatrix& operator=( const DenseMatrix& B ); // copies B ~DenseMatrix( void ); // destructor SparseMatrix sparse( void ); // converts to a sparse matrix int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val = 0. ); // sets all elements to val double norm( NormType type = lInfinity ) const; // returns the maximum magnitude of any entry T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element of the matrix (uses 0-based indexing) T& operator()( int index ); T operator()( int index ) const; // access the specified element of a vector (uses 0-based indexing) DenseMatrix transpose( void ) const; // returns the transpose of this matrix DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c DenseMatrix operator+( const DenseMatrix& B ) const; // returns sum of this matrix with B void operator+=( const DenseMatrix& B ); // adds B to this matrix DenseMatrix operator-( const DenseMatrix& B ) const; // returns difference of this matrix with B void operator-=( const DenseMatrix& B ); // subtracts B from this matrix DenseMatrix operator-( void ) const; // returns additive inverse of this matrix cholmod_dense* to_cholmod( void ); // returns pointer to copy of matrix in CHOLMOD format const DenseMatrix& operator=( cholmod_dense* B ); // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B void normalize( void ); // divides by Frobenius norm T sum( void ) const; // returns the sum of all entries void removeMean( void ); // removes the mean void randomize( void ); // replaces entries with uniformly distributed random real numbers in the interval [-1,1] protected: int m, n; std::vector data; cholmod_dense* cData; }; template DenseMatrix operator*( const DenseMatrix& A, const T& c ); // right scalar multiplication template DenseMatrix operator*( const T& c, const DenseMatrix& A ); // left scalar multiplication template DenseMatrix operator/( const DenseMatrix& A, const T& c ); // scalar division template T dot( const DenseMatrix& x, const DenseMatrix& y ); // returns Euclidean inner product of x and y template std::ostream& operator << (std::ostream& os, const DenseMatrix& o); // prints entries template T inner( const DenseMatrix& x, const DenseMatrix& y ); // standard inner product template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ); // inner product with respect to a diagonal inner // product B represented as a dense vector } #include "DenseMatrix.inl" #endif ================================================ FILE: Fairing/include/DiscreteExteriorCalculus.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DiscreteExteriorCalculus.h // ----------------------------------------------------------------------------- // // Static methods for building the fundamental discrete operators (exterior // derivative, Hodge star) for 0-, 1-, and 2-forms on a surface mesh. Methods // are templated on entry type, i.e., one can build either real- or complex- // matrices using the types DDG::Real and DDG::Complex, respectively. For // instance, to build the usual Laplacian on functions, one could write // // Mesh mesh; // SparseMatrix d0, star0, star1, Delta; // // ExteriorDerivative0Form::build( mesh, d0 ); // HodgeStar0Form::build( mesh, star0 ); // HodgeStar1Form::build( mesh, star1 ); // Delta = star0.inverse() * d0.transpose() * star1 * d0; // #ifndef DDG_DISCRETEEXTERIORCALCULUS_H #define DDG_DISCRETEEXTERIORCALCULUS_H #include "Mesh.h" #include "SparseMatrix.h" namespace DDG { template< class T > struct HodgeStar0Form { static void build( const Mesh& mesh, SparseMatrix& star0 ); }; template< class T > struct HodgeStar1Form { static void build( const Mesh& mesh, SparseMatrix& star1 ); }; template< class T > struct HodgeStar2Form { static void build( const Mesh& mesh, SparseMatrix& star2 ); }; template< class T > struct ExteriorDerivative0Form { static void build( const Mesh& mesh, SparseMatrix& d0 ); }; template< class T > struct ExteriorDerivative1Form { static void build( const Mesh& mesh, SparseMatrix& d1 ); }; } #include "DiscreteExteriorCalculus.inl" #endif ================================================ FILE: Fairing/include/Edge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Edge.h // ----------------------------------------------------------------------------- // // Edge stores attributes associated with a mesh edge. The iterator he points // to one of its two associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_EDGE_H #define DDG_EDGE_H #include "Types.h" namespace DDG { class Edge { public: HalfEdgeIter he; // points to one of the two halfedges associated with this edge int index; // unique integer ID in the range 0, ..., nEdges-1 Edge() : index(0) { } }; } #endif ================================================ FILE: Fairing/include/Face.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Face.h // ----------------------------------------------------------------------------- // // Face stores attributes associated with a mesh edge. The iterator he points // to one of its associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_FACE_H #define DDG_FACE_H #include "Types.h" namespace DDG { class Face { public: HalfEdgeIter he; // points to one of the halfedges associated with this face int index; // unique integer ID in the range 0, ..., nFaces-1 Face() : index(0) { } bool isBoundary( void ) const; // returns true if this face corresponds to a // boundary loop; false otherwise double area( void ) const; // returns the triangle area Vector normal( void ) const; // returns the unit normal associated with this face; normal // orientation is determined by the circulation order of halfedges Vector circumcenter( void ) const; // returns triangle circumcenter Vector barycenter( void ) const; // returns triangle barycenter }; } #endif ================================================ FILE: Fairing/include/HalfEdge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- HalfEdge.h // ----------------------------------------------------------------------------- // // HalfEdge is used to define mesh connectivity. (See the documentation for a // more in-depth discussion of the halfedge data structure.) // #ifndef DDG_HALFEDGE_H #define DDG_HALFEDGE_H #include "Vector.h" #include "Types.h" namespace DDG { class HalfEdge { public: HalfEdgeIter next; // points to the next halfedge around the current face HalfEdgeIter flip; // points to the other halfedge associated with this edge VertexIter vertex; // points to the vertex at the "tail" of this halfedge EdgeIter edge; // points to the edge associated with this halfedge FaceIter face; // points to the face containing this halfedge bool onBoundary; // true if this halfedge is contained in a boundary // loop; false otherwise Vector texcoord; // texture coordinates associated with the triangle corner at the // "tail" of this halfedge double cotan( void ) const; // returns the cotangent of the angle opposing this edge Vector rotatedEdge( void ) const; // returns oriented edge vector rotated by PI/2 around face normal // if onBoundary, then return nil }; } #endif ================================================ FILE: Fairing/include/Image.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Image.h // ----------------------------------------------------------------------------- // // Image represents a color bitmap image. A simple example might look like // // Image im; // im.read( "input.tga" ); // // modify image data via im(x,y) = ...; // im.write( "output.tga" ); // #ifndef DDG_IMAGE_H #define DDG_IMAGE_H #include #include namespace DDG { class Image { public: Image( int width = 0, int height = 0 ); // constructs image with specified width and height float& operator()( int x, int y ); const float& operator()( int x, int y ) const; // accesses pixel (x,y) float sample( float x, float y ) const; // samples image at (x,y) using bilinear filtering int width( void ) const; int height( void ) const; // returns image dimensions void read( const char* filename ); // loads an image file in Truevision TGA format // (must be RGB image with 24 or 32 bits per pixel) void write( const char* filename ) const; // writes an image file in Truevision TGA format // (RGB image with 24 bits per pixel) protected: void clamp( int& x, int& y ) const; // clamps coordinates to range [0,w-1] x [0,h-1] int w, h; // width and height std::vector pixels; // interleaved RGBA pixel data in range [0-1] }; } #endif ================================================ FILE: Fairing/include/LinearContext.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearContext.h // ----------------------------------------------------------------------------- // // LinearContext is the global solver context needed to interface with the // SuiteSparse library. It is essentially a wrapper around cholmod_common. A // single static instance of LinearContext is declared in LinearContext.cpp and // is shared by all instances of DenseMatrix, SparseMatrix, and LinearSystem. // In other words, you shouldn't have to instantiate LinearContext yourself // unless you're doing something really fancy! // #ifndef DDG_LINEARSOLVERCONTEXT #define DDG_LINEARSOLVERCONTEXT #include namespace DDG { class LinearContext { public: LinearContext( void ); // constructor ~LinearContext( void ); // destructor operator cholmod_common*( void ); // allows LinearContext to be treated as a cholmod_common* protected: cholmod_common context; }; } #endif ================================================ FILE: Fairing/include/LinearEquation.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearEquation.h // ----------------------------------------------------------------------------- // // LinearEquation represents an equation with an arbitrary linear polynomial on // both the left- and right-hand side. It is primarily used while building a // LinearSystem. For convenience, operator== is overloaded so that the user // can construct a LinearEquation by writing something that looks much like the // usual mathematical syntax for a linear equation. For example, // // LinearEquation eqn = ( x + 2*y == 3*z ); // // builds the linear equation x + 2y = 3z. // #ifndef DDG_LINEAREQUATION_H #define DDG_LINEAREQUATION_H #include "LinearPolynomial.h" namespace DDG { class LinearEquation { public: LinearPolynomial lhs; // left-hand side LinearPolynomial rhs; // right-hand side }; LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ); // constructs a linear equation with the specified left- and right-hand side } #endif ================================================ FILE: Fairing/include/LinearPolynomial.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearPolynomial.h // ----------------------------------------------------------------------------- // // LinearPolynomial represents an affine function of the form // // f(x1,x2,...,xn) = c1 x1 + c2 x2 + ... + cn xn + d // // where the xi are real-valued variables with real coefficients ci, and d is // a real constant. The variables and their coefficients are represented using // instances of the Variable class. LinearPolynomial implements all the usual // algebraic operations on affine functions, as well as type conversions from // more elementary types (scalars, single variables, etc.). // // Importantly, variables used in a LinearPolynomial should *not* be deallocated // while the polynomial is still in use -- LinearPolynomial stores only a // reference to these variables so that the solution to a linear system can be // automatically copied back into the variables. // #ifndef DDG_LINEARPOLYNOMIAL_H #define DDG_LINEARPOLYNOMIAL_H #include #include #include "Variable.h" namespace DDG { class LinearPolynomial { public: LinearPolynomial( void ); // constructs the zero function LinearPolynomial( double c ); // constructs the constant function with value c LinearPolynomial( Variable& v ); // constructs a function with a single variable v const LinearPolynomial& operator=( double c ); // assigns the constant function with value c const LinearPolynomial& operator=( Variable& v ); // assigns a function with a single variable v void operator+=( double c ); void operator-=( double c ); void operator*=( double c ); void operator/=( double c ); // adds, subtract, multiplies, or divides by a constant void operator+=( Variable& v ); void operator-=( Variable& v ); // increments or decrements by a single variable v void operator+=( const LinearPolynomial& p ); void operator-=( const LinearPolynomial& p ); // increments or decrements by an affine function LinearPolynomial operator-( void ) const; // returns the additive inverse (i.e., negation) double evaluate( void ) const; // evaluates the function using the current values of its variables std::map linearTerms; // list of linear terms double constantTerm; // constant term }; LinearPolynomial operator+( double c, Variable& v ); // sum LinearPolynomial operator+( Variable& v, double c ); // sum LinearPolynomial operator-( double c, Variable& v ); // difference LinearPolynomial operator-( Variable& v, double c ); // difference LinearPolynomial operator*( double c, Variable& v ); // product LinearPolynomial operator*( Variable& v, double c ); // product LinearPolynomial operator/( Variable& v, double c ); // quotient // algebraic operations between single variables and constants LinearPolynomial operator+( Variable& v1, Variable& v2 ); // sum LinearPolynomial operator-( Variable& v1, Variable& v2 ); // difference // algebraic operations between pairs of variables LinearPolynomial operator+( const LinearPolynomial& p, double c ); // sum LinearPolynomial operator+( double c, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, double c ); // difference LinearPolynomial operator-( double c, const LinearPolynomial& p ); // difference LinearPolynomial operator*( const LinearPolynomial& p, double c ); // product LinearPolynomial operator*( double c, const LinearPolynomial& p ); // product LinearPolynomial operator/( const LinearPolynomial& p, double c ); // quotient // algebraic operations between polynomials and constants LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ); // sum LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ); // difference LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ); // difference // algebraic operations between polynomials and single variables LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ); // sum LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ); // difference // algebraic operations between pairs of polynomials std::ostream& operator<<( std::ostream& os, const LinearPolynomial& p ); // prints the symbolic representation of a polynomial (all variables must be named) } #endif ================================================ FILE: Fairing/include/LinearSystem.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearSystem.h // ----------------------------------------------------------------------------- // // LinearSystem represents a system of linear equations expressed in terms of // instances of the Variable class. The main idea is to make it easy to // construct and solve linear systems without explicitly think about variable // indices, matrix layout, etc. (This kind of abstraction is particularly // useful for debugging and rapid prototyping.) See the documentation for // examples of building linear and solving systems. // // Importantly, any variable used by a LinearSystem should not be deallocated // while the system is still in use, because the method LinearSystem::solve() // automatically copies the solution back into the variables used to define the // equations. (In the future variables may become reference-counted in order // to avoid this issue.) // // Note that LinearSystem::solve() uses a general-purpose linear solver (namely // the sparse QR factorization found in SuiteSparse) that is quite fast but may // not always be your best option. To improve performance you may want to // build the system explicitly using an instance of SparseMatrix and call a // more specialized solver. (In the future there may be options for specifying // that a LinearSystem is, e.g., symmetric and positive-definite.) // #ifndef DDG_LINEARSYSTEM_H #define DDG_LINEARSYSTEM_H #include #include "LinearEquation.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "Real.h" namespace DDG { class LinearSystem { public: void clear( void ); // removes all equations from the system void push_back( const LinearEquation& e ); // appends the equation e to the sytem void solve( void ); // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution std::vector equations; // the collection of equations defining the system protected: void convertEquations( void ); void indexVariables( void ); void buildSparseMatrix( void ); void buildRightHandSide( void ); void computeSolution( void ); int nEquations; int nVariables; std::vector currentEquations; std::map index; SparseMatrix A; DenseMatrix x; DenseMatrix b; }; } #endif ================================================ FILE: Fairing/include/Mesh.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Mesh.h // ----------------------------------------------------------------------------- // // Mesh represents a polygonal surface mesh using the halfedge data structure. // It is essentially a large collection of disjoint vertices, edges, and faces // that are ``glued together'' by halfedges which encode connectivity (see // the documentation for an illustration). By construction, the halfedge data // structure cannot represent nonorientable surfaces or meshes with nonmanifold // edges. // // Mesh elements are referenced using iterators -- common usage of these // iterators is to either traverse an entire vector of mesh elements: // // // visit all vertices // for( VertexIter i = vertices.begin(); i != vertices.end(); i++ ) // { // //... // } // // or to perform a local traversal over the neighborhood of some mesh element: // // // visit both halfedges of edge e // HalfEdgeIter he = e->he; // do // { // // ... // // he = he->flip; // } // while( he != e->he ); // // (See Types.h for an explicit definition of iterator types.) // // Meshes with boundary are handled by creating an additional face for each // boundary loop (the method Face::isBoundary() determines whether a given // face is a boundary loop). Isolated vertices (i.e., vertiecs not contained // in any edge or face) reference a dummy halfedge and can be checked via // the method Vertex::isIsolated(). // #ifndef DDG_MESH_H #define DDG_MESH_H #include #include #include "HalfEdge.h" #include "Vertex.h" #include "Edge.h" #include "Face.h" #include "SparseMatrix.h" namespace DDG { class Mesh { public: Mesh( void ); // constructs an empty mesh Mesh( const Mesh& mesh ); // constructs a copy of mesh const Mesh& operator=( const Mesh& mesh ); // copies mesh int read( const std::string& filename ); // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error int write( const std::string& filename ) const; // writes a mesh to a Wavefront OBJ file; return value is nonzero // only if there was an error bool reload( void ); // reloads a mesh from disk using the most recent input filename void normalize( void ); // centers around the origin and rescales to have unit radius double area( void ) const; // returns total mesh area double meanEdgeLength( void ) const; // returns mean edge lenght std::vector halfedges; std::vector vertices; std::vector edges; std::vector faces; std::vector boundaries; // storage for mesh elements protected: std::string inputFilename; void indexElements( void ); // assigns a unique, 0-based index to each mesh element }; } #endif ================================================ FILE: Fairing/include/MeshIO.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- MeshIO.h // ----------------------------------------------------------------------------- // // MeshIO handles input/output operations for Mesh objects. Currently the only // supported mesh format is Wavefront OBJ -- for a format specification see // // http://en.wikipedia.org/wiki/Wavefront_.obj_file // // Note that vertex normals and material properties are currently ignored. // #ifndef DDG_MESHIO_H #define DDG_MESHIO_H #include #include #include #include namespace DDG { class Mesh; class Index; class MeshData; class MeshIO { public: static int read( std::istream& in, Mesh& mesh ); // reads a mesh from a valid, open input stream in static void write( std::ostream& out, const Mesh& mesh ); // writes a mesh to a valid, open output stream out protected: static int readMeshData( std::istream& in, MeshData& data ); static void readPosition( std::stringstream& ss, MeshData& data ); static void readTexCoord( std::stringstream& ss, MeshData& data ); static void readNormal ( std::stringstream& ss, MeshData& data ); static void readFace ( std::stringstream& ss, MeshData& data ); static Index parseFaceIndex( const std::string& token ); static void preallocateMeshElements( const MeshData& data, Mesh& mesh ); static int buildMesh( const MeshData& data, Mesh& mesh ); static void checkIsolatedVertices( const Mesh& Mesh ); static void checkNonManifoldVertices( const Mesh& Mesh ); }; } #endif ================================================ FILE: Fairing/include/Quaternion.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Quaternion.h // ----------------------------------------------------------------------------- // // Quaternion represents an element of the quaternions, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // Hamilton product is expressed using the * operator: // // Quaternion p, q, r; // r = q * p; // // and conjugation is expressed using the method Quaternion::conj(): // // Quaternion q; // double normQSquared = -q.conj()*q; // // Individual components can be accessed in several ways: the real and imaginary // parts can be accessed using the methods Quaternion::re() and Quaternion::im(): // // Quaternion q; // double a = q.re(); // Vector b = q.im(); // // or by index: // // Quaternion q; // double a = q[0]; // double bi = q[1]; // double bj = q[2]; // double bk = q[3]; // #ifndef DDG_QUATERNION_H #define DDG_QUATERNION_H #include "Vector.h" #include "Complex.h" #include namespace DDG { class Quaternion { public: Quaternion( void ); // initializes all components to zero Quaternion( const Quaternion& q ); // initializes from existing quaternion Quaternion( double s, double vi, double vj, double vk ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s, const Vector& v ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s ); // initializes purely real quaternion with specified real (s) component (imaginary part is zero) Quaternion( const Vector& v ); // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) Quaternion( const Complex& z ); // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k const Quaternion& operator=( double s ); // assigns a purely real quaternion with real value s const Quaternion& operator=( const Vector& v ); // assigns a purely real quaternion with imaginary value v double& operator[]( int index ); // returns reference to the specified component (0-based indexing: r, i, j, k) const double& operator[]( int index ) const; // returns const reference to the specified component (0-based indexing: r, i, j, k) void toMatrix( double Q[4][4] ) const; // builds 4x4 matrix Q representing (left) quaternion multiplication double& re( void ); // returns reference to double part const double& re( void ) const; // returns const reference to double part Vector& im( void ); // returns reference to imaginary part const Vector& im( void ) const; // returns const reference to imaginary part Quaternion operator+( const Quaternion& q ) const; // addition Quaternion operator-( const Quaternion& q ) const; // subtraction Quaternion operator-( void ) const; // negation Quaternion operator*( double c ) const; // right scalar multiplication Quaternion operator/( double c ) const; // scalar division void operator+=( const Quaternion& q ); // addition / assignment void operator+=( double c ); // addition / assignment of pure real void operator-=( const Quaternion& q ); // subtraction / assignment void operator-=( double c ); // subtraction / assignment of pure real void operator*=( double c ); // scalar multiplication / assignment void operator/=( double c ); // scalar division / assignment Quaternion operator*( const Quaternion& q ) const; // Hamilton product void operator*=( const Quaternion& q ); // Hamilton product / assignment Quaternion conj( void ) const; // conjugation Quaternion inv( void ) const; // inverse double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Quaternion unit( void ) const; // returns unit quaternion void normalize( void ); // divides by Euclidean length protected: double s; // scalar (double) part Vector v; // vector (imaginary) part }; Quaternion operator*( double c, const Quaternion& q ); // left scalar multiplication std::ostream& operator<<( std::ostream& os, const Quaternion& q ); // prints components } #endif ================================================ FILE: Fairing/include/Real.h ================================================ #ifndef DDG_REAL_H #define DDG_REAL_H namespace DDG { class Real { public: Real( double x = 0. ); // constructs real number with value x operator double( void ) const; // type cast to double void operator+=( double x ); // increment void operator-=( double x ); // decrement void operator*=( double x ); // multiply void operator/=( double x ); // divide Real conj( void ) const; // simply returns the value (for compatibility w/ complex numbers) Real inv( void ) const; // returns inverse double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Real unit( void ) const; // returns number with unit norm and same sign protected: double value; // value }; } #endif ================================================ FILE: Fairing/include/Shader.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Shader.h // ----------------------------------------------------------------------------- // // Shader encapsulates the functionality of a shader program written in // the OpenGL Shader Language (GLSL). Basic usage is to read a collection // of source files to disk and enable the shader before making draw calls. // For instance, during initialization one might write // // Shader shader; // shader.loadVertex( "vertex.glsl" ); // shader.loadFragment( "fragment.glsl" ); // // and in the main draw routine write // // shader.enable(); // // draw some stuff // shader.disable(); // #ifndef DDG_SHADER_H #define DDG_SHADER_H #include #include namespace DDG { class Shader { public: Shader( void ); // constructor -- shader is initially invalid ~Shader( void ); // destructor void loadVertex( const char* filename ); // read vertex shader from GLSL source file void loadFragment( const char* filename ); // read fragment shader from GLSL source file void loadGeometry( const char* filename ); // read geometry shader from GLSL source file void enable( void ); // uses this shader for rendering void disable( void ) const; // uses the fixed-function pipeline for rendering operator GLuint( void ) const; // returns the ID of this shader program (for calls to OpenGL) protected: void load( GLenum shaderType, const char* filename, GLuint& shader ); bool readSource( const char* filename, std::string& source ); GLuint vertexShader; GLuint fragmentShader; GLuint geometryShader; GLuint program; bool linked; }; } #endif ================================================ FILE: Fairing/include/SparseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- SparseMatrix.h // ----------------------------------------------------------------------------- // // SparseMatrix represents an m by n (real or complex) matrix where only // nonzero entries are stored explicitly. This class is most commonly // used to represent the linear term in sparse linear systems (i.e., the matrix // part). // // A real or complex matrix is allocated via // // SparseMatrix A( m, n ); // SparseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // SparseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a SparseMatrix returns a // cholmod_sparse* which can be used by routines in SuiteSparse. For basic // operations, however, you should not need to access this pointer explicitly -- // see the solve() method below. // // Internally SparseMatrix stores nonzero entries in a heap data structure; the // amortized cost of insertion is therefore no worse than the sorting cost of // putting the matrix in compressed-column order. // #ifndef DDG_SPARSE_MATRIX_H #define DDG_SPARSE_MATRIX_H #include #include #include #include "Types.h" namespace DDG { template class SparseMatrix { public: SparseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix SparseMatrix( const SparseMatrix& B ); // copy constructor ~SparseMatrix( void ); // destructor const SparseMatrix& operator=( const SparseMatrix& B ); // copies B const SparseMatrix& operator=( cholmod_sparse* B ); // copies a cholmod_sparse* into a SparseMatrix; // takes responsibility for deallocating B void resize( int m, int n ); // clears and resizes to mxn matrix SparseMatrix transpose( void ) const; // returns the transpose of this matrix cholmod_sparse* to_cholmod( void ); // returns pointer to copy of matrix in compressed-column CHOLMOD format SparseMatrix operator*( const SparseMatrix& B ) const; // returns product of this matrix with sparse B DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with dense B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c void operator+=( const SparseMatrix& B ); // adds B to this matrix void operator-=( const SparseMatrix& B ); // subtracts B from this matrix SparseMatrix operator+( const SparseMatrix& B ) const; // returns sum of this matrix with B SparseMatrix operator-( const SparseMatrix& B ) const; // returns difference of this matrix with B int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val ); // sets all nonzero elements val SparseMatrix inverse( void ) const; // returns inverse -- for diagonal matrices only // (assertion occurs for non-diagonal matrices) static SparseMatrix identity( int N ); // returns the N x N identity matrix DenseMatrix full( void ) const; // converts to a dense matrix T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element (uses 0-based indexing) // TODO for legibility, replace w/ type where entries are named "row, // TODO col" instead of "first, second" (especially since we adopt the // TODO unorthodox convention of storing the column first) typedef std::pair EntryIndex; // convenience type for an entry index; note that we store column THEN // row, which makes it easier to build compressed column format typedef std::map EntryMap; typedef typename EntryMap::iterator iterator; typedef typename EntryMap::const_iterator const_iterator; // convenience types for storing and accessing entries iterator begin( void ); const_iterator begin( void ) const; iterator end( void ); const_iterator end( void ) const; // return iterators to first and last nonzero entries void shift( double c ); // adds c times the identity matrix to this matrix protected: int m, n; EntryMap data; cholmod_sparse* cData; void allocateSparse( void ); void setEntry( const_iterator e, int i, double* pr ); }; template SparseMatrix operator*( const SparseMatrix& A, const T& c ); // right scalar multiplication template SparseMatrix operator*( const T& c, const SparseMatrix& A ); // left scalar multiplication template SparseMatrix operator/( const SparseMatrix& A, const T& c ); // scalar division template std::ostream& operator << (std::ostream& os, const SparseMatrix& o); // prints entries template class SparseFactor { public: SparseFactor( void ); ~SparseFactor( void ); void build( SparseMatrix& A ); // factorizes positive-definite matrix A using CHOLMOD bool valid( void ) const; // returns true if the factor has been built; false otherwise cholmod_factor* to_cholmod( void ); // returns pointer to underlying cholmod_factor data structure protected: cholmod_factor *L; }; template void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse QR factorization template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse LU factorization template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ); // backsolves the prefactored positive definite sparse linear system LL'x = b template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ); // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ); // returns the max residual of the linear problem A x = b relative to the largest entry of the solution template double residual( const SparseMatrix& A, const DenseMatrix& x ); // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda B x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns /<(B-EE^T)x,x> } #include "SparseMatrix.inl" #endif ================================================ FILE: Fairing/include/Types.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Types.h // ----------------------------------------------------------------------------- // // This file contains forward declarations of common types and definitions of // convenience types for standard iterators. // #ifndef DDG_TYPES_H #define DDG_TYPES_H #include #include #include namespace DDG { // forward declarations class Camera; class Complex; class Edge; class Face; class HalfEdge; class Image; class LinearContext; class LinearEquation; class LinearPolynomial; class LinearSystem; class Mesh; class MeshIO; class Quaternion; class Real; class Shader; class Variable; class Vector; class Vertex; class Viewer; template class DenseMatrix; template class SparseMatrix; // convenience types for iterators typedef std::map::iterator TermIter; typedef std::map::const_iterator TermCIter; typedef std::vector::iterator PolyIter; typedef std::vector::const_iterator PolyCIter; typedef std::vector::iterator EqnIter; typedef std::vector::const_iterator EqnCIter; typedef std::map::iterator IndexIter; typedef std::map::const_iterator IndexCIter; typedef std::vector::iterator HalfEdgeIter; typedef std::vector::const_iterator HalfEdgeCIter; typedef std::vector::iterator VertexIter; typedef std::vector::const_iterator VertexCIter; typedef std::vector::iterator EdgeIter; typedef std::vector::const_iterator EdgeCIter; typedef std::vector::iterator FaceIter; typedef std::vector::const_iterator FaceCIter; } #endif ================================================ FILE: Fairing/include/Utility.h ================================================ #ifndef DDG_UTILITY_H #define DDG_UTILITY_H #include #include "Utility.h" #include "Complex.h" namespace DDG { inline double sqr( double x ) { return x*x; } inline double unitRand( void ) { const double rRandMax = 1. / (double) RAND_MAX; return rRandMax * (double) rand(); } inline double seconds( int t0, int t1 ) { return (double)(t1-t0) / (double) CLOCKS_PER_SEC; } } namespace DDGConstants { static DDG::Complex ii( 0., 1. ); } #endif ================================================ FILE: Fairing/include/Variable.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Variable.h // ----------------------------------------------------------------------------- // // Variable represents a variable that can be used to define a (linear or // nonlinear) system of equations. Its main feature is that it can be used // both as an abstract variable (e.g., when used to define an equation) but can // also store a definite numerical value. For instance, suppose we define a // linear polynomial // // Variable x, y; // LinearPolynomial p = x + 2*y; // // Now by assigning different numerical values to x and y, we can evaluate the // polynomial at different points: // // *x = 1; // *y = 2; // cout << p.evaluate() << endl; // // *x = 3; // *y = 4; // cout << p.evaluate() << endl; // // In general the dereference operator (*) accesses the numerical value. // Variables can also be named in order to aid with debugging. For instance, // // Variable x( "x" ); // Variable y( "y" ); // Polynomial p = x + 2*y; // cout << p << endl; // // will print out something like "x+2*y". // // The "fixed" flag in a variable refers to whether it is held constant while // solving a system of equations -- see the documentation for further discussion. // #ifndef DDG_VARIABLE_H #define DDG_VARIABLE_H #include namespace DDG { class Variable { public: Variable( double value = 0., bool fixed = false ); // initialize a variable which has value zero and is not fixed by default Variable( std::string name, double value = 0., bool fixed = false ); // initialize a named variable which has value zero and is not fixed by default double& operator*( void ); // returns a reference to the numerical value const double& operator*( void ) const; // returns a const reference to the numerical value std::string name; // names the variable (for display output) double value; // numerical value bool fixed; // true if a variable is held constant while solving a system of equations }; } #endif ================================================ FILE: Fairing/include/Vector.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vector.h // ----------------------------------------------------------------------------- // // Vector represents an element of Euclidean 3-space, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // inner product (i.e., scalar or dot product) is expressed using the global // method dot(): // // Vector u, v; // double cosTheta = dot( u, v ); // // and the cross product is expressed using the global method cross(): // // Vector u, v, w; // w = cross( u, v ); // // Individual components can be accessed in two ways: either directly via the // members x, y, and z: // // Vector v; // cout << v.x << endl; // cout << v.y << endl; // cout << v.z << endl; // // or by index: // // Vector v; // for( int i = 0; i < 3; i++ ) // { // cout << v[i] << endl; // } // #ifndef DDG_VECTOR_H #define DDG_VECTOR_H #include namespace DDG { class Vector { public: Vector(); // initializes all components to zero Vector( double x, double y, double z); // initializes with specified components Vector( const Vector& v ); // initializes from existing vector double& operator[] ( const int& index ); // returns reference to the specified component (0-based indexing: x, y, z) const double& operator[] ( const int& index ) const; // returns const reference to the specified component (0-based indexing: x, y, z) Vector operator+( const Vector& v ) const; // addition Vector operator-( const Vector& v ) const; // subtraction Vector operator-( void ) const; // negation Vector operator*( const double& c ) const; // right scalar multiplication Vector operator/( const double& c ) const; // scalar division void operator+=( const Vector& v ); // addition / assignment void operator-=( const Vector& v ); // subtraction / assignment void operator*=( const double& c ); // scalar multiplication / assignment void operator/=( const double& c ); // scalar division / assignment double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Vector unit( void ) const; // returns unit vector void normalize( void ); // divides by Euclidean length Vector abs( void ) const; // returns vector containing magnitude of each component double x, y, z; // components }; Vector operator* ( const double& c, const Vector& v ); // left scalar multiplication double dot( const Vector& u, const Vector& v ); // dot product (a.k.a. inner or scalar product) Vector cross( const Vector& u, const Vector& v ); // cross product std::ostream& operator << (std::ostream& os, const Vector& o); // prints components } #endif ================================================ FILE: Fairing/include/Vertex.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vertex.h // ----------------------------------------------------------------------------- // // Vertex stores attributes associated with a mesh edge. The iterator he // points to its "outgoing" halfedge. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_VERTEX_H #define DDG_VERTEX_H #include "Vector.h" #include "Types.h" namespace DDG { class Vertex { public: HalfEdgeIter he; // points to the "outgoing" halfedge Vector position; // location of vertex in Euclidean 3-space int index; // unique integer ID in the range 0, ..., nVertices-1 bool tag; // true if vertex is selected by the user; false otherwise Vertex() : index(0), tag(false) { } double area( void ) const; // returns the barycentric area associated with this vertex Vector normal( void ) const; // returns the vertex normal bool isIsolated( void ) const; // returns true if the vertex is not contained in any face or edge; false otherwise int valence( void ) const; // returns the number of incident faces / edges void toggleTag(); // toggle vertex tag }; } #endif ================================================ FILE: Fairing/include/Viewer.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Viewer.h // ----------------------------------------------------------------------------- // // Viewer provides a graphical user interface (GUI) for inspecting and // interacting with a Mesh object. Viewer methods are static in order // to make them compatible with GLUT callbacks. // #ifndef DDG_VIEWER_H #define DDG_VIEWER_H #include #include "Mesh.h" #include "Camera.h" #include "Shader.h" namespace DDG { class Viewer { public: static void init( void ); // displays the viewer until the program ends static Mesh mesh; // surface mesh visualized by Viewer protected: // init static void initGLUT( void ); static void initGLSL( void ); // GLUT callbacks static void display( void ); static void idle( void ); static void keyboard( unsigned char c, int x, int y ); static void special( int i, int x, int y ); static void mouse( int button, int state, int x, int y ); static void motion( int x, int y ); static void menu( int value ); static void view( int value ); // menu functions static void mProcess( void ); static void mResetMesh( void ); static void mWriteMesh( void ); static void mExit( void ); static void mWireframe( void ); static void mZoomIn( void ); static void mZoomOut( void ); static void mScreenshot( void ); static void mIncreaseStep( void ); static void mDecreaseStep( void ); // unique identifiers for menus enum { menuProcess, menuResetMesh, menuWriteMesh, menuExit, menuWireframe, menuZoomIn, menuZoomOut, menuScreenshot, menuIncreaseStep, menuDecreaseStep }; // draw routines static void setGL( void ); static void setLighting( void ); static void setMeshMaterial( void ); static void callDisplayList( void ); static void updateDisplayList( void ); static void drawScene( void ); static void drawPolygons( void ); static void drawWireframe( void ); static void drawVertices( void ); static void drawSelectedVertices( void ); static void drawIsolatedVertices( void ); static void pickVertex(int x, int y); static void storeViewerState( void ); static void restoreViewerState( void ); static int windowSize[2]; static bool renderWireframe; // draw wireframe static Camera camera; // keeps track of view state static GLuint surfaceDL; // display list for mesh static Shader shader; // shader used to determine appearance of surface static double step; static double delta; // fairing time step }; } #endif ================================================ FILE: Fairing/obj/.empty ================================================ ================================================ FILE: Fairing/shaders/fragment.glsl ================================================ uniform vec3 eye; uniform vec3 light; varying vec3 position; varying vec3 normal; float diffuse( vec3 N, vec3 L ) { return max( 0., dot( N, L )); } float specular( vec3 N, vec3 L, vec3 E ) { const float shininess = 8.; vec3 R = 2.*dot(L,N)*N - L; return pow( max( 0., dot( R, E )), shininess ); } float fresnel( vec3 N, vec3 E ) { const float sharpness = 10.; float NE = max( 0., dot( N, E )); return pow( sqrt( 1. - NE*NE ), sharpness ); } void main() { vec3 N = normalize( normal ); vec3 L = normalize( light - position ); vec3 E = normalize( eye - position ); vec3 R = 2.*dot(L,N)*N - L; vec3 one = vec3( 1., 1., 1. ); gl_FragColor.rgb = diffuse(N,L)*gl_Color.rgb + .5*specular(N,L,E)*one + .5*fresnel(N,E)*one; gl_FragColor.a = 1.; } ================================================ FILE: Fairing/shaders/vertex.glsl ================================================ varying vec3 position; varying vec3 normal; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_FrontColor = gl_Color; position = gl_Vertex.xyz; normal = gl_Normal.xyz; } ================================================ FILE: Fairing/src/Camera.cpp ================================================ #include #include #include using namespace std; #include "Camera.h" namespace DDG { Camera :: Camera( void ) : pClick( 1. ), pDrag( 1. ), pLast( 1. ), rLast( 1. ), momentum( 1. ), zoom( 1. ) {} Quaternion Camera :: clickToSphere( int x, int y ) { GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); int w = viewport[2]; int h = viewport[3]; Quaternion p( 0., 2. * (double) x / (double) w - 1., 2. * (double) y / (double) h - 1., 0. ); if( p.norm2() > 1. ) { p.normalize(); p.im().z = 0.; } else { p.im().z = sqrt( 1. - p.norm2() ); } return p; } Quaternion Camera :: currentRotation( void ) const { return ( pDrag * pClick.conj() ) * rLast; } void Camera :: setView( void ) const { Quaternion r = ( pDrag * pClick.conj() ) * rLast; double w = r[0]; double x = r[1]; double y = r[2]; double z = r[3]; GLdouble M[16] = { 1.-2.*y*y-2.*z*z, 2.*x*y+2.*w*z, 2.*x*z-2.*w*y, 0., 2.*x*y-2.*w*z, 1.-2.*x*x-2.*z*z, 2.*y*z+2.*w*x, 0., 2.*x*z+2.*w*y, 2.*y*z-2.*w*x, 1.-2.*x*x-2.*y*y, 0., 0., 0., 0., 1. }; glMatrixMode( GL_MODELVIEW ); glMultMatrixd( M ); } void Camera :: mouse( int button, int state, int x, int y ) { if( state == GLUT_DOWN ) { pClick = pDrag = pLast = clickToSphere( x, y ); momentum = 1.; } if( state == GLUT_UP ) { double timeSinceDrag = ( clock() - tLast ) / (double) CLOCKS_PER_SEC; if( timeSinceDrag < .1 ) { momentum = pDrag * pLast.conj(); momentum = ( .03 * momentum + .97 ).unit(); } else { momentum = 1.; } rLast = pDrag * pClick.conj() * rLast; pClick = pDrag = 1.; } } void Camera :: motion( int x, int y ) { tLast = clock(); pLast = pDrag; pDrag = clickToSphere( x, y ); } void Camera :: idle( void ) { // get time since last idle event static int t0 = clock(); int t1 = clock(); double dt = (t1-t0) / (double) CLOCKS_PER_SEC; rLast = momentum * rLast; momentum = ( (1.-.5*dt) * momentum + .5*dt ).unit(); zoom += vZoom*dt; vZoom *= max( 0., 1.-5.*dt ); t0 = t1; } void Camera :: zoomIn( void ) { vZoom -= 0.5; } void Camera :: zoomOut( void ) { vZoom += 0.5; } } ================================================ FILE: Fairing/src/Complex.cpp ================================================ #include "Complex.h" #include #include using namespace std; namespace DDG { Complex::Complex( double a, double b ) // constructs number a+bi : re( a ), im( b ) {} void Complex::operator+=( const Complex& z ) // add z { re += z.re; im += z.im; } void Complex::operator-=( const Complex& z ) // subtract z { re -= z.re; im -= z.im; } void Complex::operator*=( const Complex& z ) // Complex multiply by z { double a = re; double b = im; double c = z.re; double d = z.im; re = a*c-b*d; im = a*d+b*c; } void Complex::operator*=( double r ) // scalar multiply by r { re *= r; im *= r; } void Complex::operator/=( double r ) // scalar divide by r { re /= r; im /= r; } void Complex::operator/=( const Complex& z ) // scalar divide by r { *this *= z.inv(); } Complex Complex::operator-( void ) const { return Complex( -re, -im ); } Complex Complex::conj( void ) const // returns Complex conjugate { return Complex( re, -im ); } Complex Complex::inv( void ) const // returns inverse { return this->conj() / this->norm2(); } double Complex::arg( void ) const // returns argument { return atan2( im, re ); } double Complex::norm( void ) const // returns norm { return sqrt( re*re + im*im ); } double Complex::norm2( void ) const // returns norm squared { return re*re + im*im; } Complex Complex::unit( void ) const // returns complex number with unit norm and same modulus { return *this / this->norm(); } Complex Complex::exponential( void ) const // complex exponentiation { return exp( re ) * Complex( cos( im ), sin( im )); } Complex operator+( const Complex& z1, const Complex& z2 ) // binary addition { Complex z = z1; z += z2; return z; } Complex operator-( const Complex& z1, const Complex& z2 ) // binary subtraction { Complex z = z1; z -= z2; return z; } Complex operator*( const Complex& z1, const Complex& z2 ) // binary Complex multiplication { Complex z = z1; z *= z2; return z; } Complex operator*( const Complex& z, double r ) // right scalar multiplication { Complex zr = z; zr *= r; return zr; } Complex operator*( double r, const Complex& z ) // left scalar multiplication { return z*r; } Complex operator/( const Complex& z, double r ) // scalar division { Complex zr = z; zr /= r; return zr; } Complex operator/( const Complex& z1, const Complex& z2 ) // complex division { Complex z = z1; z /= z2; return z; } double dot( const Complex& z1, const Complex& z2 ) { return z1.re*z2.re + z1.im*z2.im; } double cross( const Complex& z1, const Complex& z2 ) { return z1.re*z2.im - z1.im*z2.re; } std::ostream& operator<<( std::ostream& os, const Complex& z ) // prints components { if( z.im > 0 ) { os << z.re << " + " << z.im << "i"; } else if( z.im < 0 ) { os << z.re << " - " << -z.im << "i"; } else { os << z.re; } return os; } } ================================================ FILE: Fairing/src/DenseMatrix.cpp ================================================ #include "DenseMatrix.h" namespace DDG { template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i] = data[i]; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_COMPLEX, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i*2+0] = data[i].re; x[i*2+1] = data[i].im; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { assert( nColumns() == 1 ); if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } cData = cholmod_l_allocate_dense( m*4, 1, m*4, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { x[i*4+k] = data[i][k]; } } return cData; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = x[i]; } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = Complex( x[i*2+0], x[i*2+1] ); } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); assert( B->ncol == 1 ); assert( B->nrow%4 == 0 ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow/4; n = 1; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m; i++ ) { data[i] = Quaternion( x[i*4+0], x[i*4+1], x[i*4+2], x[i*4+3] ); } return *this; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 3; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { double x = o(i,j); if( x == 0. ) { os << " 0"; for( int k = 0; k < p+6; k++ ) { os << " "; } } else if( x > 0. ) { os << " " << x << " "; } else { os << x << " "; } } os << "]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { Complex z = o(i,j); if( z.re == 0. ) { os << " 0"; for( int k = 0; k < p+5; k++ ) { os << " "; } } else if( z.re > 0. ) { os << " " << z.re; } else { os << z.re; } if( z.im == 0 ) { os << " "; } else if( z.im >= 0 ) { os << "+"; } else { os << "-"; } if( z.im == 0. ) { for( int k = 0; k < p+8; k++ ) { os << " "; } } else { os << abs( z.im ) << "i "; } } os << " ]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "["; for( int j = 0; j < o.nColumns(); j++ ) { Quaternion q = o(i,j); os << " " << q; } os << " ]" << endl; } return os; } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i] = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i].re = 2.*unitRand() - 1.; data[i].im = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { data[i][k] = 2.*unitRand() - 1.; } } } } ================================================ FILE: Fairing/src/DenseMatrix.inl ================================================ #include #include #include #include using namespace std; #include "DenseMatrix.h" #include "LinearContext.h" #include "Quaternion.h" #include "SparseMatrix.h" #include "Utility.h" namespace DDG { extern LinearContext context; template DenseMatrix :: DenseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) { data.resize( m*n ); zero(); } template DenseMatrix :: DenseMatrix( const DenseMatrix& A ) // copy constructor : cData( NULL ) { *this = A; } template DenseMatrix :: ~DenseMatrix( void ) // destructor { if( cData != NULL ) { cholmod_l_free_dense( &cData, context ); } } template DenseMatrix DenseMatrix :: transpose( void ) const { const DenseMatrix& A( *this ); DenseMatrix AT( n, m ); for( int i = 0; i < n; i++ ) for( int j = 0; j < m; j++ ) { AT(i,j) = A(j,i).conj(); } return AT; } template SparseMatrix DenseMatrix::sparse( void ) // converts to a sparse matrix { SparseMatrix B; B = cholmod_l_dense_to_sparse( this->to_cholmod(), true, context ); return B; } template DenseMatrix DenseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); DenseMatrix AB( A.nRows(), B.nColumns() ); for( int i = 0; i < A.nRows(); i++ ) for( int j = 0; j < B.nColumns(); j++ ) for( int k = 0; k < A.nColumns(); k++ ) { AB( i, j ) += A( i, k ) * B( k, j ); } return AB; } template void DenseMatrix :: operator*=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c; } } template void DenseMatrix :: operator/=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c.inv(); } } template DenseMatrix DenseMatrix :: operator+( const DenseMatrix& B ) const // returns sum of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) + B(i,j); } return C; } template void DenseMatrix :: operator+=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) += B(i,j); } } template DenseMatrix DenseMatrix :: operator-( const DenseMatrix& B ) const // returns difference of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) - B(i,j); } return C; } template void DenseMatrix :: operator-=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) -= B(i,j); } } template DenseMatrix operator*( const T& c, const DenseMatrix& A ) { DenseMatrix cA = A; cA *= c; return cA; } template DenseMatrix operator*( const DenseMatrix& A, double c ) { return c*A; } template DenseMatrix operator/( const DenseMatrix& A, double c ) { DenseMatrix Ac = A; Ac /= c; return Ac; } template const DenseMatrix& DenseMatrix :: operator=( const DenseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template int DenseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int DenseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int DenseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void DenseMatrix :: zero( const T& val ) // sets all elements to val { for( int i = 0; i < m*n; i++ ) { data[i] = val; } } template double DenseMatrix :: norm( NormType type ) const { double r = 0.; if( type == lInfinity ) { for( int i = 0; i < m*n; i++ ) { r = max( r, data[i].norm() ); } } else if( type == lOne ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm(); } } else if( type == lTwo ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm2(); } r = sqrt( r ); } return r; } template void DenseMatrix :: normalize( void ) // divides by l2 norm { *this /= norm( lTwo ); } template T& DenseMatrix :: operator()( int row, int col ) { return data[row+m*col]; } template T DenseMatrix :: operator()( int row, int col ) const { return data[row+m*col]; } template T& DenseMatrix :: operator()( int index ) { return data[index]; } template T DenseMatrix :: operator()( int index ) const { return data[index]; } template T DenseMatrix::sum( void ) const // returns the sum of all entries { T total( 0., 0. ); for( int i = 0; i < m*n; i++ ) { total += data[i]; } return total; } template void DenseMatrix :: removeMean( void ) { T mean = 0.; int N = m*n; for( int i = 0; i < N; i++ ) { mean += data[i]; } mean /= (double) N; for( int i = 0; i < N; i++ ) { data[i] -= mean; } } template T dot( const DenseMatrix& x, const DenseMatrix& y ) // returns Euclidean inner product of x and y { return ( x.transpose() * y )(0); } template DenseMatrix DenseMatrix::operator-( void ) const // returns additive inverse of this matrix { const DenseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { B( i, j ) = -A( i, j ); } return B; } template T inner( const DenseMatrix& x, const DenseMatrix& y ) // standard inner product { T sum = 0.; assert( x.nRows() == y.nRows() && x.nColumns() == y.nColumns() ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * y(i); } return sum; } template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ) // inner product with respect a diagonal inner // product B represented as a dense vector { T sum = 0.; assert( x.nRows() == y.nRows() && x.nRows() == B.nRows() && x.nColumns() == 1 && B.nColumns() == 1 && y.nColumns() == 1 ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * B(i) * y(i); } return sum; } } ================================================ FILE: Fairing/src/DiscreteExteriorCalculus.inl ================================================ #include "DiscreteExteriorCalculus.h" namespace DDG { template void HodgeStar0Form :: build( const Mesh& mesh, SparseMatrix& star0 ) // builds a diagonal matrix mapping primal discrete 0-forms // to dual discrete 2-forms { int nV = mesh.vertices.size(); star0 = SparseMatrix( nV, nV ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { int i = v->index; star0( i, i ) = v->area(); } } template void HodgeStar1Form :: build( const Mesh& mesh, SparseMatrix& star1 ) // builds a diagonal matrix mapping primal discrete 1-forms // to dual discrete 1-forms { int nE = mesh.edges.size(); star1 = SparseMatrix( nE, nE ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // get the cotangents of the two angles opposite this edge double cotAlpha = e->he->cotan(); double cotBeta = e->he->flip->cotan(); int i = e->index; star1( i, i ) = ( cotAlpha + cotBeta ) / 2.; } } template void HodgeStar2Form :: build( const Mesh& mesh, SparseMatrix& star2 ) // builds a diagonal matrix mapping primal discrete 2-forms // to dual discrete 2-forms { int nF = mesh.faces.size(); star2 = SparseMatrix( nF, nF ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { int i = f->index; star2( i, i ) = 1. / f->area(); } } template< class T > void ExteriorDerivative0Form :: build( const Mesh& mesh, SparseMatrix& d0 ) { int nV = mesh.vertices.size(); int nE = mesh.edges.size(); d0 = SparseMatrix( nE, nV ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // the row index is the index of the edge int r = e->index; // the column indices are the indices of the two // edge vertices -- orientation is determined by // the orientation of the edge's first half edge int ci = e->he->vertex->index; int cj = e->he->flip->vertex->index; d0( r, ci ) = -1.; d0( r, cj ) = 1.; } } template< class T > void ExteriorDerivative1Form :: build( const Mesh& mesh, SparseMatrix& d1 ) { int nE = mesh.edges.size(); int nF = mesh.faces.size(); d1 = SparseMatrix( nF, nE ); // visit each face for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { // the row index is the index of the face int r = f->index; // visit all edges of this face HalfEdgeCIter he = f->he; do { // the column index is the index of the current edge int c = he->edge->index; // relative orientation is determined by checking if // the current half edge is the first half edge of its // corresponding edge double s = ( he->edge->he == he ? 1. : -1. ); // set the entry for this edge d1( r, c ) = s; he = he->next; } while( he != f->he ); } } } ================================================ FILE: Fairing/src/Edge.cpp ================================================ #include "Edge.h" #include "Mesh.h" namespace DDG { } ================================================ FILE: Fairing/src/Face.cpp ================================================ #include "Face.h" #include "Mesh.h" #include "Vector.h" namespace DDG { double Face::area( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).norm() / 2.; } Vector Face::normal( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).unit(); } bool Face::isBoundary( void ) const { return he->onBoundary; } Vector Face :: circumcenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector n = he->rotatedEdge(); double h = 0.5*he->cotan(); return 0.5*(p0+p1) + h*n; } Vector Face :: barycenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return (p0 + p1 + p2)/3.; } } ================================================ FILE: Fairing/src/HalfEdge.cpp ================================================ #include "HalfEdge.h" #include "Mesh.h" namespace DDG { double HalfEdge :: cotan( void ) const { if( onBoundary ) return 0.0; Vector p0 = next->next->vertex->position; Vector p1 = vertex->position; Vector p2 = next->vertex->position; Vector u = p1-p0; Vector v = p2-p0; return dot( u, v ) / cross( u, v ).norm(); } Vector HalfEdge :: rotatedEdge( void ) const { if( onBoundary ) return Vector(); Vector n = face->normal(); Vector p0 = vertex->position; Vector p1 = flip->vertex->position; return cross( n, p1-p0 ); } } ================================================ FILE: Fairing/src/Image.cpp ================================================ #include #include #include #include using namespace std; #include "Image.h" namespace DDG { Image :: Image( int width, int height ) : w( width ), h( height ), pixels( w*h*3 ) {} float& Image :: operator()( int x, int y ) // accesses pixel (x,y) { return pixels[ x + y*w ]; } const float& Image :: operator()( int x, int y ) const // accesses pixel (x,y) { return pixels[ x + y*w ]; } float Image :: sample( float x, float y ) const // samples image at (x,y) using bilinear filtering { const Image& I( *this ); float ax = x - floor( x ); float ay = y - floor( y ); float bx = 1. - ax; float by = 1. - ay; int x0 = (int) floor( x ); int y0 = (int) floor( y ); int x1 = x0 + 1; int y1 = y0 + 1; clamp( x0, y0 ); clamp( x1, y1 ); return by * ( bx * I(x0,y0) + ax * I(x1,y0) ) + ay * ( bx * I(x0,y1) + ax * I(x1,y1) ) ; } int Image :: width( void ) const // returns image width { return w; } int Image :: height( void ) const // returns image height { return h; } class TGAHeader // header format for Truevision TGA images { public: char idFieldSize; char colorMapType; char dataTypeCode; short colorMapOrigin; short colorMapLength; char colorMapEntrySize; short xOrigin; short yOrigin; short width; short height; char bitsPerPixel; char imageSpecification; }; void Image :: read( const char* filename ) // loads an image file in Truevision TGA format // (must be uncompressed RGB image with 24 or 32 bits per pixel) { ifstream in( filename, ios_base::binary ); if( !in.is_open() ) { cerr << "Error: could not open file " << filename << " for input!" << endl; exit( 1 ); } // read header TGAHeader header; in.read( (char*) &(header.idFieldSize), 1 ); in.read( (char*) &(header.colorMapType), 1 ); in.read( (char*) &(header.dataTypeCode), 1 ); in.read( (char*) &(header.colorMapOrigin), 2 ); in.read( (char*) &(header.colorMapLength), 2 ); in.read( (char*) &(header.colorMapEntrySize), 1 ); in.read( (char*) &(header.xOrigin), 2 ); in.read( (char*) &(header.yOrigin), 2 ); in.read( (char*) &(header.width), 2 ); in.read( (char*) &(header.height), 2 ); in.read( (char*) &(header.bitsPerPixel), 1 ); in.read( (char*) &(header.imageSpecification), 1 ); w = header.width; h = header.height; // validate data type const char uncompressedRGB = 2; if( header.dataTypeCode != uncompressedRGB || ( header.bitsPerPixel != 24 && header.bitsPerPixel != 32 )) { cerr << "Error: input must be uncompressed RGB image with 24 or 32 bits per pixel." << endl; exit( 1 ); } // read identification field (unused) vector idField( header.idFieldSize ); in.read( &idField[0], header.idFieldSize ); // read color map data (unused) if( header.colorMapType == 1 ) { int bytesPerEntry = header.colorMapEntrySize / 8; int colorMapSize = header.colorMapLength * bytesPerEntry; vector colorMapData( colorMapSize ); in.read( &colorMapData[0], colorMapSize ); } // read pixel data int n = w*h*header.bitsPerPixel/8; vector pixelData( n ); in.read( (char*) &pixelData[0], n ); // convert pixel data to floating point pixels.resize( n ); for( int i = 0; i < n; i++ ) { pixels[i] = (double) pixelData[i] / 255.; } } void Image :: write( const char* filename ) const // writes an image file in Truevision TGA format // (uncompressed RGB image with 24 bits per pixel) { ofstream out( filename, ios_base::binary ); if( !out.is_open() ) { cerr << "Error: could not open file " << filename << " for output!" << endl; exit( 1 ); } TGAHeader header; header.idFieldSize = 0; header.colorMapType = 0; header.dataTypeCode = 2; header.colorMapOrigin = 0; header.colorMapLength = 0; header.colorMapEntrySize = 0; header.xOrigin = 0; header.yOrigin = 0; header.width = w; header.height = h; header.bitsPerPixel = 24; header.imageSpecification = 0; // write header out.write( (char*) &(header.idFieldSize), 1 ); out.write( (char*) &(header.colorMapType), 1 ); out.write( (char*) &(header.dataTypeCode), 1 ); out.write( (char*) &(header.colorMapOrigin), 2 ); out.write( (char*) &(header.colorMapLength), 2 ); out.write( (char*) &(header.colorMapEntrySize), 1 ); out.write( (char*) &(header.xOrigin), 2 ); out.write( (char*) &(header.yOrigin), 2 ); out.write( (char*) &(header.width), 2 ); out.write( (char*) &(header.height), 2 ); out.write( (char*) &(header.bitsPerPixel), 1 ); out.write( (char*) &(header.imageSpecification), 1 ); // convert pixel data from floating point vector pixelData( w*h*3 ); for( int i = 0; i < w*h*3; i++ ) { pixelData[i] = (unsigned char)( pixels[i] * 255. ); } // write pixel data out.write( (char*) &pixelData[0], w*h*3 ); } void Image :: clamp( int& x, int& y ) const // clamps coordinates to range [0,w-1] x [0,h-1] { x = max( 0, min( w-1, x )); y = max( 0, min( h-1, y )); } } ================================================ FILE: Fairing/src/LinearContext.cpp ================================================ #include "LinearContext.h" namespace DDG { // global context for linear solvers LinearContext context; LinearContext :: LinearContext( void ) // constructor { cholmod_l_start( &context ); } LinearContext :: ~LinearContext( void ) // destructor { cholmod_l_finish( &context ); } LinearContext :: operator cholmod_common*( void ) // allows LinearContext to be treated as a cholmod_common* { return &context; } } ================================================ FILE: Fairing/src/LinearEquation.cpp ================================================ #include "LinearEquation.h" namespace DDG { LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ) // constructs a linear equation with the specified left- and right-hand side { LinearEquation eqn; eqn.lhs = lhs; eqn.rhs = rhs; return eqn; } } ================================================ FILE: Fairing/src/LinearPolynomial.cpp ================================================ #include using namespace std; #include "LinearPolynomial.h" #include "Types.h" namespace DDG { LinearPolynomial :: LinearPolynomial( void ) : constantTerm( 0. ) {} LinearPolynomial :: LinearPolynomial( double c ) { *this = c; } LinearPolynomial :: LinearPolynomial( Variable& v ) { *this = v; } const LinearPolynomial& LinearPolynomial :: operator=( double c ) { linearTerms.clear(); constantTerm = c; return *this; } const LinearPolynomial& LinearPolynomial :: operator=( Variable& v ) { linearTerms.clear(); linearTerms[ &v ] = 1.; constantTerm = 0.; return *this; } void LinearPolynomial::operator+=( double c ) { constantTerm += c; } void LinearPolynomial::operator-=( double c ) { constantTerm -= c; } void LinearPolynomial::operator*=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second *= c; } constantTerm *= c; } void LinearPolynomial::operator/=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second /= c; } constantTerm /= c; } void LinearPolynomial::operator+=( Variable& v ) { LinearPolynomial p( v ); *this += p; } void LinearPolynomial::operator-=( Variable& v ) { LinearPolynomial p( v ); *this -= p; } void LinearPolynomial::operator+=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second += i->second; } else { linearTerms[i->first] = i->second; } } constantTerm += e.constantTerm; } void LinearPolynomial::operator-=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second -= i->second; } else { linearTerms[i->first] = -i->second; } } constantTerm -= e.constantTerm; } LinearPolynomial LinearPolynomial::operator-( void ) const { LinearPolynomial p = *this; for( TermIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { i->second = -i->second; } p.constantTerm = -p.constantTerm; return p; } double LinearPolynomial::evaluate( void ) const { double value = constantTerm; for( TermCIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { value += i->second * i->first->value; } return value; } LinearPolynomial operator+( double c, Variable& v ) { return c + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, double c ) { return LinearPolynomial(v) + c; } LinearPolynomial operator-( double c, Variable& v ) { return c - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, double c ) { return LinearPolynomial(v) - c; } LinearPolynomial operator*( double c, Variable& v ) { return LinearPolynomial(v) * c; } LinearPolynomial operator*( Variable& v, double c ) { return LinearPolynomial(v) * c; } LinearPolynomial operator/( Variable& v, double c ) { return LinearPolynomial(v) / c; } LinearPolynomial operator+( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) + LinearPolynomial(v2); } LinearPolynomial operator-( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) - LinearPolynomial(v2); } LinearPolynomial operator+( const LinearPolynomial& p, double c ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator+( double c, const LinearPolynomial& p ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, double c ) { LinearPolynomial difference = p; difference -= c; return difference; } LinearPolynomial operator-( double c, const LinearPolynomial& p ) { LinearPolynomial difference = -p; difference += c; return difference; } LinearPolynomial operator*( const LinearPolynomial& p, double c ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator*( double c, const LinearPolynomial& p ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator/( const LinearPolynomial& p, double c ) { LinearPolynomial quotient = p; quotient /= c; return quotient; } LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ) { return p + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) + p; } LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ) { return p - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) - p; } LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial sum = p; sum += q; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial difference = p; difference -= q; return difference; } ostream& operator<<( ostream& os, const LinearPolynomial& p ) { for( TermCIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { os << i->second << "*" << i->first->name << " + "; } os << p.constantTerm; return os; } } ================================================ FILE: Fairing/src/LinearSystem.cpp ================================================ #include using namespace std; #include #include "LinearSystem.h" #include "LinearContext.h" #include "Types.h" namespace DDG { extern LinearContext context; void LinearSystem::clear( void ) // removes all equations from the system { equations.clear(); } void LinearSystem::push_back( const LinearEquation& e ) // appends the equation e to the sytem { equations.push_back( e ); } void LinearSystem::solve( void ) // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution { convertEquations(); indexVariables(); buildSparseMatrix(); buildRightHandSide(); computeSolution(); } void LinearSystem::convertEquations( void ) // converts each equation to its polynomial representation { currentEquations.clear(); for( EqnIter eqn = equations.begin(); eqn != equations.end(); eqn ++ ) { // move right-hand side to left-hand side LinearPolynomial p = eqn->lhs - eqn->rhs; // convert fixed variables to constants LinearPolynomial q( p.constantTerm ); for( TermIter t = p.linearTerms.begin(); t != p.linearTerms.end(); t ++ ) { const double& coefficient( t->second ); Variable& variable( *(t->first) ); // skip zeros if( coefficient == 0. ) continue; if( t->first->fixed ) { q += coefficient * variable.value; } else { q += coefficient * variable; } } if( q.linearTerms.size() > 0 ) { currentEquations.push_back( q ); } } nEquations = currentEquations.size(); } void LinearSystem::indexVariables( void ) // assign a unique index to each variable remaining in one of the polynomials { index.clear(); nVariables = 0; for( PolyCIter p = currentEquations.begin(); p != currentEquations.end(); p ++ ) { for( TermCIter t = p->linearTerms.begin(); t != p->linearTerms.end(); t ++ ) { IndexIter j = index.find( t->first ); // if we haven't seen this variable // before, assign it a unique index if( j == index.end() ) { index[ t->first ] = nVariables; nVariables++; } } } } void LinearSystem::buildSparseMatrix( void ) // build the sparse matrix representation of our current system { A = SparseMatrix( nEquations, nVariables ); for( int i = 0; i < nEquations; i++ ) { for( TermCIter t = currentEquations[i].linearTerms.begin(); t != currentEquations[i].linearTerms.end(); t ++ ) { int j = index[ t->first ]; A(i,j) = t->second; } } } void LinearSystem::buildRightHandSide( void ) // build the data vector for our current system { b = DenseMatrix( nEquations, 1 ); for( int i = 0; i < nEquations; i++ ) { b(i) = -currentEquations[i].constantTerm; } } void LinearSystem::computeSolution( void ) { // solve linear system Ax=b x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); // put solution values in variables for( IndexIter i = index.begin(); i != index.end(); i ++ ) { i->first->value = x( i->second ); } } } ================================================ FILE: Fairing/src/Mesh.cpp ================================================ #include #include #include "Mesh.h" #include "MeshIO.h" #include "DiscreteExteriorCalculus.h" using namespace std; namespace DDG { Mesh :: Mesh( void ) {} Mesh :: Mesh( const Mesh& mesh ) { *this = mesh; } class HalfEdgeIterCompare { public: bool operator()( const HalfEdgeIter& i, const HalfEdgeIter& j ) const { return &*i < &*j; } }; class HalfEdgeCIterCompare { public: bool operator()( const HalfEdgeCIter& i, const HalfEdgeCIter& j ) const { return &*i < &*j; } }; class VertexIterCompare { public: bool operator()( const VertexIter& i, const VertexIter& j ) const { return &*i < &*j; } }; class VertexCIterCompare { public: bool operator()( const VertexCIter& i, const VertexCIter& j ) const { return &*i < &*j; } }; class FaceIterCompare { public: bool operator()( const FaceIter& i, const FaceIter& j ) const { return &*i < &*j; } }; class FaceCIterCompare { public: bool operator()( const FaceCIter& i, const FaceCIter& j ) const { return &*i < &*j; } }; class EdgeIterCompare { public: bool operator()( const EdgeIter& i, const EdgeIter& j ) const { return &*i < &*j; } }; class EdgeCIterCompare { public: bool operator()( const EdgeCIter& i, const EdgeCIter& j ) const { return &*i < &*j; } }; const Mesh& Mesh :: operator=( const Mesh& mesh ) { map< HalfEdgeCIter, HalfEdgeIter, HalfEdgeCIterCompare > halfedgeOldToNew; map< VertexCIter, VertexIter, VertexCIterCompare > vertexOldToNew; map< EdgeCIter, EdgeIter, EdgeCIterCompare > edgeOldToNew; map< FaceCIter, FaceIter, FaceCIterCompare > faceOldToNew; // copy geometry from the original mesh and create a // map from pointers in the original mesh to // those in the new mesh halfedges.clear(); for( HalfEdgeCIter he = mesh.halfedges.begin(); he != mesh.halfedges.end(); he++ ) halfedgeOldToNew[ he ] = halfedges.insert( halfedges.end(), *he ); vertices.clear(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) vertexOldToNew[ v ] = vertices.insert( vertices.end(), *v ); edges.clear(); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e++ ) edgeOldToNew[ e ] = edges.insert( edges.end(), *e ); faces.clear(); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) faceOldToNew[ f ] = faces.insert( faces.end(), *f ); // "search and replace" old pointers with new ones for( HalfEdgeIter he = halfedges.begin(); he != halfedges.end(); he++ ) { he->next = halfedgeOldToNew[ he->next ]; he->flip = halfedgeOldToNew[ he->flip ]; he->vertex = vertexOldToNew[ he->vertex ]; he->edge = edgeOldToNew[ he->edge ]; he->face = faceOldToNew[ he->face ]; } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) v->he = halfedgeOldToNew[ v->he ]; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) e->he = halfedgeOldToNew[ e->he ]; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) f->he = halfedgeOldToNew[ f->he ]; return *this; } int Mesh::read( const string& filename ) { inputFilename = filename; ifstream in( filename.c_str() ); if( !in.is_open() ) { cerr << "Error reading from mesh file " << filename << endl; return 1; } int rval; if( !( rval = MeshIO::read( in, *this ))) { indexElements(); normalize(); } return rval; } int Mesh::write( const string& filename ) const // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error { ofstream out( filename.c_str() ); if( !out.is_open() ) { cerr << "Error writing to mesh file " << filename << endl; return 1; } MeshIO::write( out, *this ); return 0; } bool Mesh::reload( void ) { return read( inputFilename ); } void Mesh::normalize( void ) { // compute center of mass Vector c( 0., 0., 0. ); for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { c += v->position; } c /= (double) vertices.size(); // translate to origin for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position -= c; } // rescale such that the mesh sits inside the unit ball double rMax = 0.; for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { rMax = max( rMax, v->position.norm() ); } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position /= rMax; } } void Mesh::indexElements( void ) { int nV = 0; for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->index = nV; nV++; } int nE = 0; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) { e->index = nE; nE++; } int nF = 0; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) { f->index = nF; nF++; } } double Mesh::area( void ) const { double sum = 0.0; for( FaceCIter f = faces.begin(); f != faces.end(); f ++ ) { sum += f->area(); } return sum; } double Mesh::meanEdgeLength( void ) const { double sum = 0; for( EdgeCIter e = edges.begin(); e != edges.end(); e ++) { VertexIter v0 = e->he->vertex; VertexIter v1 = e->he->flip->vertex; sum += (v0->position - v1->position).norm(); } return sum / edges.size(); } } ================================================ FILE: Fairing/src/MeshIO.cpp ================================================ #include #include #include #include #include "MeshIO.h" #include "Mesh.h" using namespace std; namespace DDG { class Index { public: Index( void ) {} Index( int p, int t, int n ) : position( p ), texcoord( t ), normal( n ) {} bool operator<( const Index& i ) const { if( position < i.position ) return true; if( position > i.position ) return false; if( texcoord < i.texcoord ) return true; if( texcoord > i.texcoord ) return false; if( normal < i.normal ) return true; if( normal > i.normal ) return false; return false; } int position; int texcoord; int normal; }; class MeshData { public: std::vector positions; std::vector texcoords; std::vector normals; std::vector< std::vector< Index > > indices; }; int MeshIO :: read( istream& in, Mesh& mesh ) // reads a mesh from a valid, open input stream in { MeshData data; if( readMeshData( in, data )) { return 1; } if( buildMesh( data, mesh )) { return 1; } return 0; } void MeshIO :: write( ostream& out, const Mesh& mesh ) // writes a mesh to a valid, open output stream out { int currentIndex = 1; map vertexIndex; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) { out << "v " << v->position.x << " " << v->position.y << " " << v->position.z << endl; vertexIndex[ v ] = currentIndex; currentIndex++; } for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeIter he = f->he; for( int j = 0; j < 3; j++ ) { out << "vt " << he->texcoord.x << " " << he->texcoord.y << endl; he = he->next; } } for( size_t i = 0; i < mesh.faces.size(); i++ ) { const Face& f( mesh.faces[i] ); HalfEdgeIter he = f.he; out << "f "; int j = 0; do { out << vertexIndex[ he->vertex ] << "/" << 1+(i*3+j) << " "; he = he->next; j++; } while( he != f.he ); out << endl; } } int MeshIO :: readMeshData( istream& in, MeshData& data ) { string line; while( getline( in, line )) { stringstream ss( line ); string token; ss >> token; if( token == "v" ) { readPosition( ss, data ); continue; } // vertex if( token == "vt" ) { readTexCoord( ss, data ); continue; } // texture coordinate if( token == "vn" ) { readNormal ( ss, data ); continue; } // vertex normal if( token == "f" ) { readFace ( ss, data ); continue; } // face if( token[0] == '#' ) continue; // comment if( token == "o" ) continue; // object name if( token == "g" ) continue; // group name if( token == "s" ) continue; // smoothing group if( token == "mtllib" ) continue; // material library if( token == "usemtl" ) continue; // material if( token == "" ) continue; // empty string cerr << "Error: does not appear to be a valid Wavefront OBJ file!" << endl; cerr << "(Offending line: " << line << ")" << endl; return 1; } return 0; } void MeshIO :: preallocateMeshElements( const MeshData& data, Mesh& mesh ) { // count the number of edges set< pair > edges; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { for( unsigned int I = 0; I < f->size(); I++ ) { int J = (I+1) % f->size(); int i = (*f)[I].position; int j = (*f)[J].position; if( i > j ) swap( i, j ); edges.insert( pair( i, j )); } } int nV = data.positions.size(); int nE = edges.size(); int nF = data.indices.size(); int nHE = 2*nE; int chi = nV - nE + nF; int nB = max( 0, 2 - chi ); // (conservative approximation of number of boundary cycles) mesh.halfedges.clear(); mesh.vertices.clear(); mesh.edges.clear(); mesh.faces.clear(); mesh.boundaries.clear(); mesh.halfedges.reserve( nHE ); mesh.vertices.reserve( nV ); mesh.edges.reserve( nE ); mesh.faces.reserve( nF ); mesh.boundaries.reserve( nB ); } extern vector isolated; // all isolated vertices point to isolated.begin() int MeshIO :: buildMesh( const MeshData& data, Mesh& mesh ) { map< pair< int, int >, int > edgeCount; map< pair< int, int >, HalfEdgeIter > existingHalfEdges; map< int, VertexIter > indexToVertex; map< HalfEdgeIter, bool > hasFlipEdge; preallocateMeshElements( data, mesh ); // allocate a vertex for each position in the data and construct // a map from vertex indices to vertex pointers for( unsigned int i = 0; i < data.positions.size(); i++ ) { VertexIter newVertex = mesh.vertices.insert( mesh.vertices.end(), Vertex() ); newVertex->position = data.positions[ i ]; newVertex->he = isolated.begin(); indexToVertex[ i ] = newVertex; } // insert each face into the mesh int faceIndex = 0; bool degenerateFaces = false; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { int N = f->size(); // print an error if the face is degenerate if( N < 3 ) { cerr << "Error: face " << faceIndex << " is degenerate (fewer than three vertices)!" << endl; degenerateFaces = true; continue; } // create a new face FaceIter newFace = mesh.faces.insert( mesh.faces.end(), Face()); // create a new half edge for each edge of the current face vector< HalfEdgeIter > hes( N ); for( int i = 0; i < N; i++ ) { hes[ i ] = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); } // initialize these new halfedges for( int i = 0; i < N; i++ ) { // the current halfedge goes from vertex a to vertex b int a = (*f)[ i ].position; int b = (*f)[ (i+1) % N ].position; // set current halfedge's attributes hes[ i ]->next = hes[ (i+1) % N ]; hes[ i ]->vertex = indexToVertex[ a ]; int t = (*f)[i].texcoord; if( t >= 0 ) hes[ i ]->texcoord = data.texcoords[ t ]; else hes[ i ]->texcoord = Vector( 0., 0., 0. ); hes[ i ]->onBoundary = false; // keep track of which halfedges have flip edges defined (for detecting boundaries) hasFlipEdge[ hes[ i ]] = false; // point vertex a at the current halfedge indexToVertex[ a ]->he = hes[ i ]; // point the new face and this half edge to each-other hes[ i ]->face = newFace; newFace->he = hes[ i ]; // if we've created an edge between a and b in the past, it is the // flip edge of the current halfedge if( a > b ) swap( a, b ); if( existingHalfEdges.find( pair( a, b )) != existingHalfEdges.end()) { hes[ i ]->flip = existingHalfEdges[ pair( a, b ) ]; hes[ i ]->flip->flip = hes[ i ]; hes[ i ]->edge = hes[ i ]->flip->edge; hasFlipEdge[ hes[ i ]] = true; hasFlipEdge[ hes[ i ]->flip ] = true; } else // otherwise, create an edge connected to the current halfedge { hes[ i ]->edge = mesh.edges.insert( mesh.edges.end(), Edge()); hes[ i ]->edge->he = hes[i]; edgeCount[ pair( a, b ) ] = 0; } // record the fact that we've created a halfedge from a to b existingHalfEdges[ pair( a, b ) ] = hes[ i ]; // check for nonmanifold edges edgeCount[ pair( a, b ) ]++; if( edgeCount[ pair( a, b ) ] > 2 ) { cerr << "Error: edge (" << a << ", " << b << ") is nonmanifold (more than two faces sharing a single edge)!" << endl; return 1; } } faceIndex++; } // give up now if there were degenerate faces if( degenerateFaces ) { return 1; } // insert extra faces for each boundary cycle for( HalfEdgeIter currentHE = mesh.halfedges.begin(); currentHE != mesh.halfedges.end(); currentHE ++ ) { // if we find a halfedge with no flip edge defined, create // a new face and link it to the corresponding boundary cycle if( !hasFlipEdge[ currentHE ] ) { // create a new face FaceIter newBoundary = mesh.boundaries.insert( mesh.boundaries.end(), Face()); // walk along this boundary cycle vector boundaryCycle; HalfEdgeIter he = currentHE; do { // create a new halfedge on the boundary face HalfEdgeIter newHE = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); // mark only the halfedge on the boundary face as being on the boundary newHE->onBoundary = true; // link the current halfedge in the cycle to its new flip edge he->flip = newHE; // grab the next halfedge along the boundary by finding // the next halfedge around the current vertex that doesn't // have a flip edge defined HalfEdgeIter nextHE = he->next; while( hasFlipEdge[ nextHE ] ) { nextHE = nextHE->flip->next; } // set attributes for the flip edge (we'll set ->next below) newHE->flip = he; newHE->vertex = nextHE->vertex; newHE->edge = he->edge; newHE->face = newBoundary; newHE->texcoord = nextHE->texcoord; // point the new face to this half edge newBoundary->he = newHE; // keep track of all the new halfedges in the boundary cycle boundaryCycle.push_back( newHE ); // continue to walk along the cycle he = nextHE; } while( he != currentHE ); // link together the cycle of boundary halfedges unsigned int N = boundaryCycle.size(); for( unsigned int i = 0; i < N; i++ ) { boundaryCycle[ i ]->next = boundaryCycle[ (i+N-1)%N ]; hasFlipEdge[ boundaryCycle[i] ] = true; hasFlipEdge[ boundaryCycle[i]->flip ] = true; } } } // print a warning if the input has any non-terminal defects checkIsolatedVertices( mesh ); checkNonManifoldVertices( mesh ); return 0; } void MeshIO :: readPosition( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.positions.push_back( Vector( x, y, z )); } void MeshIO :: readTexCoord( stringstream& ss, MeshData& data ) { double u, v; ss >> u >> v; data.texcoords.push_back( Vector( u, v, 0. )); } void MeshIO :: readNormal( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.normals.push_back( Vector( x, y, z )); } void MeshIO :: readFace( stringstream& ss, MeshData &data ) { vector faceIndices; string token; while( ss >> token ) { faceIndices.push_back( parseFaceIndex( token )); } data.indices.push_back( faceIndices ); } Index MeshIO :: parseFaceIndex( const string& token ) { // parse indices of the form // // p/[t]/[n] // // where p is an index into positions, t is an index into // texcoords, n is an index into normals, and [.] indicates // that an index is optional stringstream in( token ); string indexstring; int indices[3] = { -1, -1, -1 }; int i = 0; while( getline( in, indexstring, '/' )) { stringstream ss( indexstring ); ss >> indices[i++]; } // decrement since indices in OBJ files are 1-based return Index( indices[0]-1, indices[1]-1, indices[2]-1 ); } void MeshIO :: checkIsolatedVertices( const Mesh& mesh ) { // print a warning if the mesh has any isolated vertices int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { cerr << "Warning: vertex " << vertexIndex << " is isolated (not contained in any face)." << endl; } vertexIndex++; } } void MeshIO :: checkNonManifoldVertices( const Mesh& mesh ) { map nIncidentFaces; for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } for( FaceCIter f = mesh.boundaries.begin(); f != mesh.boundaries.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( nIncidentFaces[v] != v->valence() ) { cerr << "Warning: vertex " << vertexIndex << " is nonmanifold." << endl; } vertexIndex++; } } } ================================================ FILE: Fairing/src/Quaternion.cpp ================================================ #include #include using namespace std; #include "Quaternion.h" namespace DDG { // CONSTRUCTORS ---------------------------------------------------------- Quaternion :: Quaternion( void ) // initializes all components to zero : s( 0. ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Quaternion& q ) // initializes from existing quaternion : s( q.s ), v( q.v ) {} Quaternion :: Quaternion( double s_, double vi, double vj, double vk ) // initializes with specified double (s) and imaginary (v) components : s( s_ ), v( vi, vj, vk ) {} Quaternion :: Quaternion( double s_, const Vector& v_ ) // initializes with specified double(s) and imaginary (v) components : s( s_ ), v( v_ ) {} Quaternion :: Quaternion( double s_ ) // initializes purely real quaternion with specified real (s) component (imaginary part is zero) : s( s_ ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Vector& v_ ) // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) : s( 0. ), v( v_ ) {} Quaternion :: Quaternion( const Complex& z ) // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k : s( z.re ), v( z.im, 0., 0. ) {} // ASSIGNMENT OPERATORS -------------------------------------------------- const Quaternion& Quaternion :: operator=( double _s ) // assigns a purely real quaternion with real value s { s = _s; v = Vector( 0., 0., 0. ); return *this; } const Quaternion& Quaternion :: operator=( const Vector& _v ) // assigns a purely real quaternion with imaginary value v { s = 0.; v = _v; return *this; } // ACCESSORS ------------------------------------------------------------- double& Quaternion::operator[]( int index ) // returns reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } const double& Quaternion::operator[]( int index ) const // returns const reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } void Quaternion::toMatrix( double Q[4][4] ) const // returns 4x4 matrix representation { Q[0][0] = s; Q[0][1] = -v.x; Q[0][2] = -v.y; Q[0][3] = -v.z; Q[1][0] = v.x; Q[1][1] = s; Q[1][2] = -v.z; Q[1][3] = v.y; Q[2][0] = v.y; Q[2][1] = v.z; Q[2][2] = s; Q[2][3] = -v.x; Q[3][0] = v.z; Q[3][1] = -v.y; Q[3][2] = v.x; Q[3][3] = s; } double& Quaternion::re( void ) // returns reference to double part { return s; } const double& Quaternion::re( void ) const // returns const reference to double part { return s; } Vector& Quaternion::im( void ) // returns reference to imaginary part { return v; } const Vector& Quaternion::im( void ) const // returns const reference to imaginary part { return v; } // VECTOR SPACE OPERATIONS ----------------------------------------------- Quaternion Quaternion::operator+( const Quaternion& q ) const // addition { return Quaternion( s+q.s, v+q.v ); } Quaternion Quaternion::operator-( const Quaternion& q ) const // subtraction { return Quaternion( s-q.s, v-q.v ); } Quaternion Quaternion::operator-( void ) const // negation { return Quaternion( -s, -v ); } Quaternion Quaternion::operator*( double c ) const // scalar multiplication { return Quaternion( s*c, v*c ); } Quaternion operator*( double c, const Quaternion& q ) // scalar multiplication { return q*c; } Quaternion Quaternion::operator/( double c ) const // scalar division { return Quaternion( s/c, v/c ); } void Quaternion::operator+=( const Quaternion& q ) // addition / assignment { s += q.s; v += q.v; } void Quaternion::operator+=( double c ) // addition / assignment of pure real { s += c; } void Quaternion::operator-=( const Quaternion& q ) // subtraction / assignment { s -= q.s; v -= q.v; } void Quaternion::operator-=( double c ) // subtraction / assignment of pure real { s -= c; } void Quaternion::operator*=( double c ) // scalar multiplication / assignment { s *= c; v *= c; } void Quaternion::operator/=( double c ) // scalar division / assignment { s /= c; v /= c; } // ALGEBRAIC OPERATIONS -------------------------------------------------- Quaternion Quaternion::operator*( const Quaternion& q ) const // Hamilton product { const double& s1( s ); const double& s2( q.s ); const Vector& v1( v ); const Vector& v2( q.v ); return Quaternion( s1*s2 - dot(v1,v2), s1*v2 + s2*v1 + cross(v1,v2) ); } void Quaternion::operator*=( const Quaternion& q ) // Hamilton product / assignment { *this = ( *this * q ); } Quaternion Quaternion::conj( void ) const // conjugation { return Quaternion( s, -v ); } Quaternion Quaternion::inv( void ) const { return ( this->conj() ) / this->norm2(); } // NORMS ----------------------------------------------------------------- double Quaternion::norm( void ) const // returns Euclidean length { return sqrt( s*s + v.x*v.x + v.y*v.y + v.z*v.z ); } double Quaternion::norm2( void ) const // returns Euclidean length squared { return s*s + dot(v,v); } Quaternion Quaternion::unit( void ) const // returns unit quaternion { return *this / norm(); } void Quaternion::normalize( void ) // divides by Euclidean length { *this /= norm(); } // GEOMETRIC OPERATIONS -------------------------------------------------- Quaternion slerp( const Quaternion& q0, const Quaternion& q1, double t ) // spherical-linear interpolation { // interpolate length double m0 = q0.norm(); double m1 = q1.norm(); double m = (1-t)*m0 + t*m1; // interpolate direction Quaternion p0 = q0 / m0; Quaternion p1 = q1 / m1; double theta = acos(( p0.conj()*p1 ).re() ); Quaternion p = ( sin((1-t)*theta)*p0 + sin(t*theta)*p1 )/sin(theta); return m*p; } // I/O ------------------------------------------------------------------------- std::ostream& operator<<( std::ostream& os, const Quaternion& q ) // prints components { os << "( " << q.re() << ", " << q.im() << " )"; return os; } } ================================================ FILE: Fairing/src/Real.cpp ================================================ #include "Real.h" #include namespace DDG { Real :: Real( double x ) // constructs real number with value x : value( x ) {} Real :: operator double( void ) const // type cast to double { return value; } void Real :: operator+=( double x ) // increment { value += x; } void Real :: operator-=( double x ) // decrement { value -= x; } void Real :: operator*=( double x ) // multiply { value *= x; } void Real :: operator/=( double x ) // divide { value /= x; } Real Real :: conj( void ) const // simply returns the value (for compatibility w/ complex numbers) { return value; } Real Real :: inv( void ) const // returns inverse { return 1. / value; } double Real :: norm( void ) const // returns norm { return fabs( value ); } double Real :: norm2( void ) const // returns norm squared { return value * value; } Real Real :: unit( void ) const // returns number with unit norm and same sign { return value / norm(); } } ================================================ FILE: Fairing/src/Shader.cpp ================================================ #include "Shader.h" #include #include using namespace std; namespace DDG { Shader::Shader( void ) // constructor -- shader is initially invalid : vertexShader( 0 ), fragmentShader( 0 ), geometryShader( 0 ), program( 0 ), linked( false ) {} Shader::~Shader( void ) { if( program ) glDeleteProgram( program ); if( vertexShader ) glDeleteShader( vertexShader ); if( fragmentShader ) glDeleteShader( fragmentShader ); if( geometryShader ) glDeleteShader( geometryShader ); } void Shader::loadVertex( const char* filename ) { load( GL_VERTEX_SHADER, filename, vertexShader ); } void Shader::loadFragment( const char* filename ) { load( GL_FRAGMENT_SHADER, filename, fragmentShader ); } void Shader::loadGeometry( const char* filename ) { #ifdef GL_GEOMETRY_SHADER_EXT load( GL_GEOMETRY_SHADER_EXT, filename, geometryShader ); #else cerr << "Error: geometry shaders not supported!" << endl; #endif } void Shader::enable( void ) { if( !linked ) { glLinkProgram( program ); linked = true; } glUseProgram( program ); } void Shader::disable( void ) const { glUseProgram( 0 ); } Shader::operator GLuint( void ) const { return program; } void Shader::load( GLenum shaderType, const char* filename, GLuint& shader ) // read vertex shader from GLSL source file, compile, and attach to program { string source; if( !readSource( filename, source )) { return; } if( program == 0 ) { program = glCreateProgram(); } if( shader != 0 ) { glDetachShader( program, shader ); } shader = glCreateShader( shaderType ); const char* source_c_str = source.c_str(); glShaderSource( shader, 1, &(source_c_str), NULL ); glCompileShader( shader ); GLint compileStatus; glGetShaderiv( shader, GL_COMPILE_STATUS, &compileStatus ); if( compileStatus == GL_TRUE ) { glAttachShader( program, shader ); linked = false; } else { GLsizei maxLength = 0; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength ); if( maxLength > 0 ) { GLchar* infoLog = new char[ maxLength ]; GLsizei length; glGetShaderInfoLog( shader, maxLength, &length, infoLog ); cerr << "GLSL Error: " << infoLog << endl; delete[] infoLog; } } } bool Shader::readSource( const char* filename, std::string& source ) // reads GLSL source file into a string { source = ""; ifstream in( filename ); if( !in.is_open() ) { cerr << "Error: could not open shader file "; cerr << filename; cerr << " for input!" << endl; return false; } string line; while( getline( in, line )) { source += line; } return true; } } ================================================ FILE: Fairing/src/SparseMatrix.cpp ================================================ #include "SparseMatrix.h" namespace DDG { template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = pr[k]; } } return *this; } template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = Complex( pr[k*2+0], pr[k*2+1] ); } } return *this; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ) { SparseMatrix A( m*4, n*4 ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; const Quaternion& q( e->second ); A(i*4+0,j*4+0) = q[0]; A(i*4+0,j*4+1) = -q[1]; A(i*4+0,j*4+2) = -q[2]; A(i*4+0,j*4+3) = -q[3]; A(i*4+1,j*4+0) = q[1]; A(i*4+1,j*4+1) = q[0]; A(i*4+1,j*4+2) = -q[3]; A(i*4+1,j*4+3) = q[2]; A(i*4+2,j*4+0) = q[2]; A(i*4+2,j*4+1) = q[3]; A(i*4+2,j*4+2) = q[0]; A(i*4+2,j*4+3) = -q[1]; A(i*4+3,j*4+0) = q[3]; A(i*4+3,j*4+1) = -q[2]; A(i*4+3,j*4+2) = q[1]; A(i*4+3,j*4+3) = q[0]; } if( cData != NULL ) { cholmod_l_free_sparse( &cData, context ); } cData = cholmod_l_copy_sparse( A.to_cholmod(), context ); return cData; } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_COMPLEX, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m*4, n*4, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i] = e->second; } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i*2+0] = e->second.re; pr[i*2+1] = e->second.im; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR< complex >( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (complex)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (quaternion)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4]/4 << "\n"; } template <> void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_zl_symbolic( n, n, Ap, Ai, Ax, NULL, &Symbolic, NULL, NULL ); umfpack_zl_numeric( Ap, Ai, Ax, NULL, Symbolic, &Numeric, NULL, NULL ); umfpack_zl_solve( UMFPACK_A, Ap, Ai, Ax, NULL, (double*) &x(0), NULL, (double*) &b(0), NULL, Numeric, NULL, NULL ); umfpack_zl_free_symbolic( &Symbolic ); umfpack_zl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } } ================================================ FILE: Fairing/src/SparseMatrix.inl ================================================ #include #include #include #include #include using namespace std; #include #include #include "Real.h" #include "Complex.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "LinearContext.h" #include "Utility.h" namespace DDG { extern LinearContext context; const int maxEigIter = 20; // number of iterations used to solve eigenvalue problems template SparseMatrix :: SparseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) {} template SparseMatrix :: SparseMatrix( const SparseMatrix& B ) // copy constructor : cData( NULL ) { *this = B; } template SparseMatrix :: ~SparseMatrix( void ) // destructor { if( cData ) { cholmod_l_free_sparse( &cData, context ); } } template const SparseMatrix& SparseMatrix :: operator=( const SparseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template SparseMatrix SparseMatrix :: transpose( void ) const { SparseMatrix AT( n, m ); for( const_iterator e = begin(); e != end(); e++ ) { int i = e->first.second; int j = e->first.first; T Aij = e->second; AT(j,i) = Aij.conj(); } return AT; } template SparseMatrix SparseMatrix :: operator*( const SparseMatrix& B ) const // returns product of this matrix with sparse B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // collect nonzeros in each row vector< vector< int > > Bcol( B.nRows() ); vector< vector< T > > Bval( B.nRows() ); for( const_iterator e = B.begin(); e != B.end(); e ++ ) { int row = e->first.second; int col = e->first.first; T val = e->second; Bcol[ row ].push_back( col ); Bval[ row ].push_back( val ); } // multiply C = A*B SparseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( size_t n = 0; n < Bcol[j].size(); n++ ) { int k = Bcol[j][n]; C( i, k ) += e->second * Bval[j][n]; } } return C; } template DenseMatrix SparseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with dense B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // multiply C = A*B DenseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( int k = 0; k < B.nColumns(); k++ ) { C( i, k ) += e->second * B( j, k ); } } return C; } template void SparseMatrix :: operator*=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second *= c; } } template void SparseMatrix :: operator/=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second /= c; } } template void SparseMatrix :: operator+=( const SparseMatrix& B ) // adds B to this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) += Bij; } } template void SparseMatrix :: operator-=( const SparseMatrix& B ) // subtracts B from this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) -= Bij; } } template SparseMatrix SparseMatrix :: operator+( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C += B; return C; } template SparseMatrix SparseMatrix :: operator-( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C -= B; return C; } template SparseMatrix operator*( const T& c, const SparseMatrix& A ) { SparseMatrix cA = A; for( typename SparseMatrix::iterator e = cA.begin(); e != cA.end(); e++ ) { e->second = c * e->second; } return cA; } template SparseMatrix operator*( const SparseMatrix& A, const T& c ) { SparseMatrix Ac = A; Ac *= c; return Ac; } template SparseMatrix operator/( const SparseMatrix& A, T c ) { SparseMatrix Ac = A; Ac /= c; return Ac; } template void SparseMatrix :: resize( int m_, int n_ ) { m = m_; n = n_; data.clear(); } template int SparseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int SparseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int SparseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void SparseMatrix :: zero( const T& val ) // sets all nonzero elements val { for( iterator i = begin(); i != end(); i++ ) { i->second = val; } } template SparseMatrix SparseMatrix :: inverse( void ) const // returns inverse -- for diagonal matrices only { assert( m == n ); // matrix must be square const SparseMatrix& A( *this ); SparseMatrix Ainv( m, m ); for( const_iterator e = begin(); e != end(); e++ ) { int r = e->first.second; int c = e->first.first; assert( r == c ); // matrix must be diagonal Ainv( r, c ) = A( r, c ).inv(); } return Ainv; } template SparseMatrix SparseMatrix :: identity( int N ) { SparseMatrix I( N, N ); for( int i = 0; i < N; i++ ) { I( i, i ) = 1.; } return I; } template DenseMatrix SparseMatrix :: full( void ) const // converts to a dense matrix { const int maxSize = 1048576; if( m*n > maxSize ) { cerr << "Error: refusing to convert sparse to dense (too big!)" << "\n"; exit( 1 ); } const SparseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < m; j++ ) { B( i, j ) = A( i, j ); } return B; } template cholmod_sparse* SparseMatrix :: to_cholmod( void ) { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } allocateSparse(); // build compressed matrix (note that EntryMap stores entries in column-major order) double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; int i = 0; int j = -1; for( const_iterator e = begin(); e != end(); e ++ ) { int c = e->first.first; if( c != j ) { for( int k = j+1; k <= c; k++ ) { jc[k] = i; } j = c; } ir[i] = e->first.second; setEntry( e, i, pr ); i++; } for( int k = j+1; k <= n; k++ ) { jc[k] = i; } return cData; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ); template T& SparseMatrix :: operator()( int row, int col ) { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { data[ index ] = T( 0. ); } return data[ index ]; } template T SparseMatrix :: operator()( int row, int col ) const { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { return T( 0. ); } return entry->second; } template typename SparseMatrix::iterator SparseMatrix :: begin( void ) { return data.begin(); } template typename SparseMatrix::const_iterator SparseMatrix :: begin( void ) const { return data.begin(); } template typename SparseMatrix::iterator SparseMatrix :: end( void ) { return data.end(); } template typename SparseMatrix::const_iterator SparseMatrix :: end( void ) const { return data.end(); } template void SparseMatrix :: shift( double c ) // adds c times the identity matrix to this matrix { assert( m == n ); SparseMatrix& A( *this ); for( int i = 0; i < m; i++ ) { A( i, i ) += c; } } template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_dl_symbolic( n, n, Ap, Ai, Ax, &Symbolic, NULL, NULL ); umfpack_dl_numeric( Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL ); umfpack_dl_solve( UMFPACK_A, Ap, Ai, Ax, (double*) &x(0), (double*) &b(0), Numeric, NULL, NULL ); umfpack_dl_free_symbolic( &Symbolic ); umfpack_dl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; cholmod_factor* L = cholmod_l_analyze( Ac, context ); cholmod_l_factorize( Ac, L, context ); x = cholmod_l_solve( CHOLMOD_A, L, b.to_cholmod(), context ); if( L ) cholmod_l_free_factor( &L, context ); int t1 = clock(); cout << "[chol] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[chol] max residual: " << residual( A, x, b ) << "\n"; } template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ) // backsolves the prefactored positive definite sparse linear system LL'x = b { x = cholmod_l_solve( CHOLMOD_A, L.to_cholmod(), b.to_cholmod(), context ); } template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess { int t0 = clock(); for( int iter = 0; iter < maxEigIter; iter++ ) { solve( A, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess { // TODO use a symmetric matrix decomposition instead of QR int t0 = clock(); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= dot( e, B*e ).norm(); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; solve( A, x, x ); x -= dot( x, Be ).conj()*e; x /= dot( x, B*x ).norm(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); for( int iter = 0; iter < maxEigIter; iter++ ) { backsolvePositiveDefinite( L, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= sqrt( dot( e, B*e ).norm() ); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; backsolvePositiveDefinite( L, x, x ); x -= dot( x, Be ).conj()*e; x /= sqrt( dot( x, B*x ).norm() ); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ) // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess { int iter; int t0 = clock(); DenseMatrix ET = E.transpose(); SparseFactor L; L.build( A ); for( iter = 0; iter < maxEigIter; iter++ ) { x = B*x - E*(ET*x); backsolvePositiveDefinite( L, x, x ); x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, E, x ) << "\n"; } template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ) // returns the max residual of the linear problem A x = b relative to the largest entry of the solution { return ( A*x - b ).norm() / b.norm(); } template double residual( const SparseMatrix& A, const DenseMatrix& x ) // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, x ); return (A*x-lambda*x).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, x ); return (A*x-lambda*(B*x)).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, E, x ); return (A*x-lambda*(B*x-E*(E.transpose()*x))).norm() / x.norm(); } template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*x)(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x))(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns /<(B-EE^T)x,x> { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x-E*(E.transpose()*x)))(0).inv(); } template std::ostream& operator<<( std::ostream& os, const SparseMatrix& o) { os.precision( 3 ); for( typename SparseMatrix::const_iterator e = o.begin(); e != o.end(); e ++ ) { int row = e->first.second; int col = e->first.first; os << "( " << row << ", " << col << " ): " << e->second << "\n"; } return os; } template SparseFactor :: SparseFactor( void ) : L( NULL ) {} template SparseFactor :: ~SparseFactor( void ) { if( L ) { cholmod_l_free_factor( &L, context ); } } template void SparseFactor :: build( SparseMatrix& A ) { if( L ) { cholmod_l_free_factor( &L, context ); L = NULL; } int t0, t1; cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; t0 = clock(); L = cholmod_l_analyze( Ac, context ); t1 = clock(); cerr << "analyze: " << seconds(t0,t1) << "s" << endl; t0 = clock(); cholmod_l_factorize( Ac, L, context ); t1 = clock(); cerr << "factorize: " << seconds(t0,t1) << "s" << endl; } template bool SparseFactor :: valid( void ) const { if( L == NULL ) { return false; } return true; } template cholmod_factor* SparseFactor :: to_cholmod( void ) { return L; } } ================================================ FILE: Fairing/src/Variable.cpp ================================================ #include "Variable.h" namespace DDG { Variable :: Variable( double value_, bool fixed_ ) // initialize a variable which has value zero and is not fixed by default : value( value_ ), fixed( fixed_ ) {} Variable :: Variable( std::string name_, double value_, bool fixed_ ) // initialize a named variable which has value zero and is not fixed by default : name( name_ ), value( value_ ), fixed( fixed_ ) {} double& Variable :: operator*( void ) // returns a reference to the numerical value { return value; } const double& Variable :: operator*( void ) const // returns a const reference to the numerical value { return value; } } ================================================ FILE: Fairing/src/Vector.cpp ================================================ #include #include "Vector.h" namespace DDG { Vector :: Vector( void ) : x( 0. ), y( 0. ), z( 0. ) {} Vector :: Vector( double x0, double y0, double z0 ) : x( x0 ), y( y0 ), z( z0 ) {} Vector :: Vector( const Vector& v ) : x( v.x ), y( v.y ), z( v.z ) {} double& Vector :: operator[]( const int& index ) { return ( &x )[ index ]; } const double& Vector :: operator[]( const int& index ) const { return ( &x )[ index ]; } Vector Vector :: operator+( const Vector& v ) const { return Vector( x + v.x, y + v.y, z + v.z ); } Vector Vector :: operator-( const Vector& v ) const { return Vector( x - v.x, y - v.y, z - v.z ); } Vector Vector :: operator-( void ) const { return Vector( -x, -y, -z ); } Vector Vector :: operator*( const double& c ) const { return Vector( x*c, y*c, z*c ); } Vector operator*( const double& c, const Vector& v ) { return v*c; } Vector Vector :: operator/( const double& c ) const { return (*this) * ( 1./c ); } void Vector :: operator+=( const Vector& v ) { x += v.x; y += v.y; z += v.z; } void Vector :: operator-=( const Vector& v ) { x -= v.x; y -= v.y; z -= v.z; } void Vector :: operator*=( const double& c ) { x *= c; y *= c; z *= c; } void Vector :: operator/=( const double& c ) { (*this) *= ( 1./c ); } double Vector :: norm( void ) const { return sqrt( norm2()); } double Vector :: norm2( void ) const { return dot( *this, *this ); } void Vector :: normalize( void ) { (*this) /= norm(); } Vector Vector :: unit( void ) const { return (*this) / norm(); } Vector Vector :: abs( void ) const { return Vector( fabs( x ), fabs( y ), fabs( z ) ); } double dot( const Vector& u, const Vector& v ) { return u.x*v.x + u.y*v.y + u.z*v.z ; } Vector cross( const Vector& u, const Vector& v ) { return Vector( u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x ); } std::ostream& operator << (std::ostream& os, const Vector& o) { os << "[ " << o.x << " " << o.y << " " << o.z << " ]"; return os; } } ================================================ FILE: Fairing/src/Vertex.cpp ================================================ #include using namespace std; #include "Vertex.h" #include "Mesh.h" #include "HalfEdge.h" namespace DDG { double Vertex::area( void ) const // returns the dual area associated with this vertex { double A = 0.; HalfEdgeCIter h = he; do { if (not h->onBoundary) A += h->face->area(); h = h->flip->next; } while( h != he ); return A / 3.; } Vector Vertex::normal( void ) const // returns the vertex normal { Vector N; HalfEdgeCIter h = he; do { if (not h->onBoundary) N += h->face->normal(); h = h->flip->next; } while( h != he ); return N.unit(); } vector isolated; // all isolated vertices point to isolated.begin() bool Vertex::isIsolated( void ) const // returns true if the vertex is not contained in any face or edge; false otherwise { return he == isolated.begin(); } int Vertex :: valence( void ) const // returns the number of incident faces { int n = 0; HalfEdgeCIter h = he; do { n++; h = h->flip->next; } while( h != he ); return n; } void Vertex :: toggleTag() { tag = !tag; } } ================================================ FILE: Fairing/src/Viewer.cpp ================================================ #include #include #include #include #include #include #include using namespace std; #include "Viewer.h" #include "Image.h" #include "Application.h" namespace DDG { // declare static member variables Mesh Viewer::mesh; GLuint Viewer::surfaceDL = 0; int Viewer::windowSize[2] = { 512, 512 }; Camera Viewer::camera; Shader Viewer::shader; bool Viewer::renderWireframe = false; double Viewer::step = 0.01; double Viewer::delta = 0.001; void Viewer :: init( void ) { restoreViewerState(); initGLUT(); setGL(); initGLSL(); updateDisplayList(); glutMainLoop(); } void Viewer :: initGLUT( void ) { int argc = 0; vector< vector > argv(1); // initialize window glutInitWindowSize( windowSize[0], windowSize[1] ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); glutInit( &argc, (char**)&argv ); glutCreateWindow( "DDG" ); // specify callbacks glutDisplayFunc ( Viewer::display ); glutIdleFunc ( Viewer::idle ); glutKeyboardFunc ( Viewer::keyboard ); glutSpecialFunc ( Viewer::special ); glutMouseFunc ( Viewer::mouse ); glutMotionFunc ( Viewer::motion ); // initialize menus int viewMenu = glutCreateMenu( Viewer::view ); glutSetMenu( viewMenu ); glutAddMenuEntry( "[f] Wireframe", menuWireframe ); glutAddMenuEntry( "[↑] Zoom In", menuZoomIn ); glutAddMenuEntry( "[↓] Zoom Out", menuZoomOut ); int mainMenu = glutCreateMenu( Viewer::menu ); glutSetMenu( mainMenu ); glutAddMenuEntry( "[space] Process Mesh", menuProcess ); glutAddMenuEntry( "[r] Reset Mesh", menuResetMesh ); glutAddMenuEntry( "[w] Write Mesh", menuWriteMesh ); glutAddMenuEntry( "[\\] Screenshot", menuScreenshot ); glutAddMenuEntry( "[esc] Exit", menuExit ); glutAddSubMenu( "View", viewMenu ); glutAttachMenu( GLUT_RIGHT_BUTTON ); } void Viewer :: initGLSL( void ) { shader.loadVertex( "shaders/vertex.glsl" ); shader.loadFragment( "shaders/fragment.glsl" ); } void Viewer :: menu( int value ) { switch( value ) { case( menuProcess ): mProcess(); break; case( menuResetMesh ): mResetMesh(); break; case( menuWriteMesh ): mWriteMesh(); break; case( menuScreenshot ): mScreenshot(); break; case( menuExit ): mExit(); break; case( menuIncreaseStep ): mIncreaseStep(); break; case( menuDecreaseStep ): mDecreaseStep(); break; default: break; } } void Viewer :: view( int value ) { switch( value ) { case( menuWireframe ): mWireframe(); break; case( menuZoomIn ): mZoomIn(); break; case( menuZoomOut ): mZoomOut(); break; default: break; } } void Viewer :: keyboard( unsigned char c, int x, int y ) { switch( c ) { case 'f': mWireframe(); break; case 'w': mWriteMesh(); break; case 'r': mResetMesh(); break; case '\\': mScreenshot(); break; case ' ': mProcess(); break; case 27: mExit(); break; case '-': mDecreaseStep(); break; case '+': mIncreaseStep(); break; default: break; } } void Viewer :: special( int i, int x, int y ) { switch( i ) { case GLUT_KEY_UP: camera.zoomIn(); break; case GLUT_KEY_DOWN: camera.zoomOut(); break; case 27: mExit(); break; default: break; } } void Viewer :: mouse( int button, int state, int x, int y ) { if( ( glutGetModifiers() & GLUT_ACTIVE_SHIFT) and state == GLUT_UP ) pickVertex(x, y); else camera.mouse( button, state, x, y ); } void Viewer :: motion( int x, int y ) { camera.motion( x, y ); } void Viewer :: idle( void ) { camera.idle(); glutPostRedisplay(); } void Viewer :: storeViewerState( void ) { ofstream out( ".viewer_state.txt" ); out << camera.rLast[0] << endl; out << camera.rLast[1] << endl; out << camera.rLast[2] << endl; out << camera.rLast[3] << endl; GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); out << view[2] << endl; out << view[3] << endl; } void Viewer :: restoreViewerState( void ) { ifstream in( ".viewer_state.txt" ); if( !in.is_open() ) return; in >> camera.rLast[0]; in >> camera.rLast[1]; in >> camera.rLast[2]; in >> camera.rLast[3]; in >> windowSize[0]; in >> windowSize[1]; } void Viewer :: mIncreaseStep( void ) { step += delta; std::cout << "Step = " << step << std::endl; } void Viewer :: mDecreaseStep( void ) { step -= delta; std::cout << "Step = " << step << std::endl; } void Viewer :: mProcess( void ) { Application app; app.run(step, mesh); updateDisplayList(); } void Viewer :: mResetMesh( void ) { mesh.reload(); updateDisplayList(); } void Viewer :: mWriteMesh( void ) { mesh.write( "out.obj" ); } void Viewer :: mExit( void ) { //storeViewerState(); exit( 0 ); } void Viewer :: mWireframe( void ) { renderWireframe = !renderWireframe; updateDisplayList(); } void Viewer :: mZoomIn( void ) { camera.zoomIn(); } void Viewer :: mZoomOut( void ) { camera.zoomOut(); } void Viewer :: mScreenshot( void ) { static int index = 0; // get window width and height GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); int w = view[2]; int h = view[3]; // get pixels Image image( w, h ); glReadPixels( 0, 0, w, h, GL_BGR, GL_FLOAT, &image(0,0) ); stringstream filename; filename << "frames/viewer" << setw(8) << setfill( '0' ) << index << ".tga"; image.write( filename.str().c_str() ); index++; } void Viewer :: display( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); shader.enable(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); double aspect = (double) viewport[2] / (double) viewport[3]; const double fovy = 50.; const double clipNear = .01; const double clipFar = 1000.; gluPerspective( fovy, aspect, clipNear, clipFar ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); Quaternion eye = Vector( 0., 0., -2.5*camera.zoom ); Quaternion center = Vector( 0., 0., 0. ); Quaternion up = Vector( 0., 1., 0. ); gluLookAt( eye[1], eye[2], eye[3], center[1], center[2], center[3], up[1], up[2], up[3] ); Quaternion r = camera.currentRotation(); eye = r.conj() * eye * r; GLint uniformEye = glGetUniformLocation( shader, "eye" ); glUniform3f( uniformEye, eye[1], eye[2], eye[3] ); Quaternion light = Vector( -1., 1., -2. ); light = r.conj() * light * r; GLint uniformLight = glGetUniformLocation( shader, "light" ); glUniform3f( uniformLight, light[1], light[2], light[3] ); camera.setView(); callDisplayList(); shader.disable(); glutSwapBuffers(); } void Viewer :: updateDisplayList( void ) { if( surfaceDL ) { glDeleteLists( surfaceDL, 1 ); surfaceDL = 0; } surfaceDL = glGenLists( 1 ); glNewList( surfaceDL, GL_COMPILE ); setMeshMaterial(); drawScene(); glEndList(); } void Viewer :: setGL( void ) { glClearColor( .5, .5, .5, 1. ); setLighting(); } void Viewer :: setLighting( void ) { GLfloat position[4] = { 20., 30., 40., 0. }; glLightfv( GL_LIGHT0, GL_POSITION, position ); glEnable( GL_LIGHT0 ); glEnable( GL_NORMALIZE ); } void Viewer :: setMeshMaterial( void ) { GLfloat diffuse[4] = { .8, .5, .3, 1. }; GLfloat specular[4] = { .3, .3, .3, 1. }; GLfloat ambient[4] = { .2, .2, .5, 1. }; glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular ); glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient ); glMaterialf ( GL_FRONT_AND_BACK, GL_SHININESS, 16. ); } void Viewer :: callDisplayList( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_DEPTH_TEST ); glEnable( GL_LIGHTING ); glCallList( surfaceDL ); glPopAttrib(); } void Viewer :: drawScene( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1., 1. ); glColor3d( 1., .5, .25 ); drawPolygons(); glDisable( GL_POLYGON_OFFSET_FILL ); if( renderWireframe ) drawWireframe(); drawIsolatedVertices(); drawSelectedVertices(); glPopAttrib(); } void Viewer :: drawPolygons( void ) { for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { if( f->isBoundary() ) continue; glBegin( GL_POLYGON ); if( renderWireframe ) { Vector N = f->normal(); glNormal3dv( &N[0] ); } HalfEdgeCIter he = f->he; do { if( not renderWireframe ) { Vector N = he->vertex->normal(); glNormal3dv( &N[0] ); } glVertex3dv( &he->vertex->position[0] ); he = he->next; } while( he != f->he ); glEnd(); } } void Viewer :: drawWireframe( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glDisable( GL_LIGHTING ); glColor4f( 0., 0., 0., 0.5 ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBegin( GL_LINES ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { glVertex3dv( &e->he->vertex->position[0] ); glVertex3dv( &e->he->flip->vertex->position[0] ); } glEnd(); glPopAttrib(); } void Viewer :: drawIsolatedVertices( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glPointSize( 5 ); glHint( GL_POINT_SMOOTH_HINT, GL_NICEST ); glEnable( GL_POINT_SMOOTH ); glColor3f( 1., 0., 0. ); glBegin( GL_POINTS ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { glVertex3dv( &v->position[0] ); } } glEnd(); glPopAttrib(); } void Viewer :: drawVertices( void ) { for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { glLoadName(v->index); glBegin(GL_POINTS); glVertex3dv( &v->position[0] ); glEnd(); } } void Viewer :: drawSelectedVertices( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable(GL_COLOR_MATERIAL); glHint( GL_POINT_SMOOTH_HINT, GL_NICEST ); glEnable( GL_POINT_SMOOTH ); glColor3f( 0., 0., 0.5 ); glPointSize( 20 ); glBegin(GL_POINTS); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->tag ) glVertex3dv( &v->position[0] ); } glEnd(); glPopAttrib(); } void Viewer :: pickVertex(int x, int y) { int width = glutGet(GLUT_WINDOW_WIDTH ); int height = glutGet(GLUT_WINDOW_HEIGHT); if( x < 0 || x >= width || y < 0 || y >= height ) return; int bufSize = mesh.vertices.size(); GLuint* buf = new GLuint[bufSize]; glSelectBuffer(bufSize, buf); GLint viewport[4]; GLdouble projection[16]; glGetIntegerv( GL_VIEWPORT, viewport ); glGetDoublev(GL_PROJECTION_MATRIX, projection); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix(x, viewport[3]-y, 10, 10, viewport); glMultMatrixd(projection); glMatrixMode(GL_MODELVIEW); glPushMatrix(); drawVertices(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); long hits = glRenderMode(GL_RENDER); int index = -1; double min_z = 1.0e100; for( long i = 0; i < hits; ++i ) { double distance = buf[4*i + 1]; if( distance < min_z ) { index = buf[4*i + 3]; min_z = distance; } } delete[] buf; if (index >= 0) { mesh.vertices[index].toggleTag(); updateDisplayList(); } } } ================================================ FILE: Fairing/src/main.cpp ================================================ #include using namespace std; #include "Viewer.h" #include "DenseMatrix.h" using namespace DDG; int main( int argc, char** argv ) { if( argc != 2 ) { cerr << "usage: " << argv[0] << " in.obj" << endl; return 1; } Viewer viewer; viewer.mesh.read( argv[1] ); viewer.init(); return 0; } ================================================ FILE: Flatten/.viewer_state.txt ================================================ 0.126663 -0.0243652 -0.989174 0.0699843 815 767 ================================================ FILE: Flatten/include/Application.h ================================================ /* * Spectral Conformal Parameterization * Patrick Mullen, Yiying Tong, Pierre Alliez, Mathieu Desbrun. * Symposium of Geometry Processing, 2008. */ #ifndef DDG_APPLICATION_H #define DDG_APPLICATION_H #include "Mesh.h" #include "Complex.h" #include "DenseMatrix.h" #include "SparseMatrix.h" #include "DiscreteExteriorCalculus.h" namespace DDG { class Application { public: void run(Mesh& mesh) { if (mesh.boundaries.empty()) { std::cout << "Mesh has no boundary" << std::endl; return; } double initial_area = mesh.area(); // Energy matrix SparseMatrix Lc; buildEnergy(mesh, Lc); // make Lc positive-definite SparseMatrix star0; HodgeStar0Form::build( mesh, star0 ); Lc += Complex(1.0e-8)*star0; // compute parameterization DenseMatrix x(Lc.nRows()); x.randomize(); smallestEigPositiveDefinite(Lc, star0, x); assignSolution(x, mesh); // rescale mesh double scale = std::sqrt( initial_area / mesh.area() ); normalizeMesh(scale, mesh); } protected: void buildEnergy(const Mesh& mesh, SparseMatrix& A) const { // Laplacian SparseMatrix d0, star1; HodgeStar1Form::build( mesh, star1 ); ExteriorDerivative0Form::build( mesh, d0 ); A = d0.transpose() * star1 * d0; // Area for( FaceCIter f = mesh.boundaries.begin(); f != mesh.boundaries.end(); f ++ ) { HalfEdgeIter he = f->he; do { int i = he->flip->vertex->index; int j = he->vertex->index; A(i,j) += -0.5*DDGConstants::ii; A(j,i) += 0.5*DDGConstants::ii; he = he->next; } while( he != f->he ); } } void assignSolution(const DenseMatrix& x, Mesh& mesh) { for( VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { int i = v->index; const Complex& z = x(i,0); v->texture.x = z.re; v->texture.y = z.im; v->texture.z = 0.0; } } void normalizeMesh(const double scale, Mesh& mesh) { Vector avg; for( VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { avg += v->texture; } avg /= mesh.vertices.size(); for( VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { v->texture = scale*(v->texture - avg); } } }; } #endif ================================================ FILE: Flatten/include/Camera.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Camera.h // ----------------------------------------------------------------------------- // // Camera is used by Viewer to keep track of the view state; it also // handles mouse input related to camera manipulation. // #ifndef DDG_CAMERA_H #define DDG_CAMERA_H #include "Quaternion.h" #ifdef __CYGWIN__ #define GLUT_DISABLE_ATEXIT_HACK #include #include #include #include #else #include #endif namespace DDG { class Camera { public: Camera( void ); // constructor Quaternion clickToSphere( int x, int y ); // projects a mous click onto the unit sphere void setView( void ) const; // applies the camera transformation to the OpenGL modelview stack void mouse( int button, int state, int x, int y ); // handles mouse clicks void motion( int x, int y ); // handles mouse drags void idle( void ); // handles camera momentum void zoomIn( void ); // moves viewer toward object void zoomOut( void ); // moves viewer away from object Quaternion currentRotation( void ) const; // returns the rotation corresponding to the current mouse state Quaternion pClick; // mouse coordinates of current click Quaternion pDrag; // mouse coordinates of current drag Quaternion pLast; // mouse coordinates of previous drag Quaternion rLast; // previous camera rotation Quaternion momentum; // camera momentum int tLast; // time of previous drag double zoom, vZoom; // zoom and zoom velocity }; } #endif ================================================ FILE: Flatten/include/Complex.h ================================================ #ifndef DDG_COMPLEX_H #define DDG_COMPLEX_H #include namespace DDG { class Complex { public: Complex( double a=0., double b=0. ); // constructs number a+bi void operator+=( const Complex& z ); // add z void operator-=( const Complex& z ); // subtract z void operator*=( const Complex& z ); // Complex multiply by z void operator*=( double r ); // scalar multiply by r void operator/=( double r ); // scalar divide by r void operator/=( const Complex& z ); // complex divide by r Complex operator-( void ) const; // returns the additive inverse Complex conj( void ) const; // returns Complex conjugate Complex inv( void ) const; // returns inverse double arg( void ) const; // returns argument double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Complex unit( void ) const; // returns complex number with unit norm and same modulus Complex exponential( void ) const; // complex exponentiation double re; // real part double im; // imaginary part }; Complex operator+( const Complex& z1, const Complex& z2 ); // binary addition Complex operator-( const Complex& z1, const Complex& z2 ); // binary subtraction Complex operator*( const Complex& z1, const Complex& z2 ); // binary Complex multiplication Complex operator*( const Complex& z, double r ); // right scalar multiplication Complex operator*( double r, const Complex& z ); // left scalar multiplication Complex operator/( const Complex& z, double r ); // scalar division Complex operator/( const Complex& z1, const Complex& z2 ); // complex division double dot( const Complex& z1, const Complex& z2 ); // inner product double cross( const Complex& z1, const Complex& z2 ); // cross product std::ostream& operator<<( std::ostream& os, const Complex& o ); // prints components } #endif ================================================ FILE: Flatten/include/DenseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DenseMatrix.h // ----------------------------------------------------------------------------- // // DenseMatrix represents an m by n (real or complex) matrix where every // entry -- including zero-valued entries -- is stored explicitly. This // class is most commonly used to represent dense vectors in sparse linear // systems (i.e., the right hand side and the solution vector). // // A real or complex matrix is allocated via // // DenseMatrix A( m, n ); // DenseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // DenseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a DenseMatrix returns a cholmod_dense* // which can be used by routines in SuiteSparse. For basic operations, however, // you should not need to access this pointer explicitly -- see the solve() // method in SparseMatrix.h. // #ifndef DDG_DENSEMATRIX_H #define DDG_DENSEMATRIX_H #include #include "Types.h" #include namespace DDG { enum NormType { lInfinity, lOne, lTwo }; template class DenseMatrix { public: DenseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix (specifying just m yields a column vector) DenseMatrix( const DenseMatrix& A ); // copy constructor const DenseMatrix& operator=( const DenseMatrix& B ); // copies B ~DenseMatrix( void ); // destructor SparseMatrix sparse( void ); // converts to a sparse matrix int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val = 0. ); // sets all elements to val double norm( NormType type = lInfinity ) const; // returns the maximum magnitude of any entry T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element of the matrix (uses 0-based indexing) T& operator()( int index ); T operator()( int index ) const; // access the specified element of a vector (uses 0-based indexing) DenseMatrix transpose( void ) const; // returns the transpose of this matrix DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c DenseMatrix operator+( const DenseMatrix& B ) const; // returns sum of this matrix with B void operator+=( const DenseMatrix& B ); // adds B to this matrix DenseMatrix operator-( const DenseMatrix& B ) const; // returns difference of this matrix with B void operator-=( const DenseMatrix& B ); // subtracts B from this matrix DenseMatrix operator-( void ) const; // returns additive inverse of this matrix cholmod_dense* to_cholmod( void ); // returns pointer to copy of matrix in CHOLMOD format const DenseMatrix& operator=( cholmod_dense* B ); // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B void normalize( void ); // divides by Frobenius norm T sum( void ) const; // returns the sum of all entries void removeMean( void ); // removes the mean void randomize( void ); // replaces entries with uniformly distributed random real numbers in the interval [-1,1] protected: int m, n; std::vector data; cholmod_dense* cData; }; template DenseMatrix operator*( const DenseMatrix& A, const T& c ); // right scalar multiplication template DenseMatrix operator*( const T& c, const DenseMatrix& A ); // left scalar multiplication template DenseMatrix operator/( const DenseMatrix& A, const T& c ); // scalar division template T dot( const DenseMatrix& x, const DenseMatrix& y ); // returns Euclidean inner product of x and y template std::ostream& operator << (std::ostream& os, const DenseMatrix& o); // prints entries template T inner( const DenseMatrix& x, const DenseMatrix& y ); // standard inner product template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ); // inner product with respect to a diagonal inner // product B represented as a dense vector } #include "DenseMatrix.inl" #endif ================================================ FILE: Flatten/include/DiscreteExteriorCalculus.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DiscreteExteriorCalculus.h // ----------------------------------------------------------------------------- // // Static methods for building the fundamental discrete operators (exterior // derivative, Hodge star) for 0-, 1-, and 2-forms on a surface mesh. Methods // are templated on entry type, i.e., one can build either real- or complex- // matrices using the types DDG::Real and DDG::Complex, respectively. For // instance, to build the usual Laplacian on functions, one could write // // Mesh mesh; // SparseMatrix d0, star0, star1, Delta; // // ExteriorDerivative0Form::build( mesh, d0 ); // HodgeStar0Form::build( mesh, star0 ); // HodgeStar1Form::build( mesh, star1 ); // Delta = star0.inverse() * d0.transpose() * star1 * d0; // #ifndef DDG_DISCRETEEXTERIORCALCULUS_H #define DDG_DISCRETEEXTERIORCALCULUS_H #include "Mesh.h" #include "SparseMatrix.h" namespace DDG { template< class T > struct HodgeStar0Form { static void build( const Mesh& mesh, SparseMatrix& star0 ); }; template< class T > struct HodgeStar1Form { static void build( const Mesh& mesh, SparseMatrix& star1 ); }; template< class T > struct HodgeStar2Form { static void build( const Mesh& mesh, SparseMatrix& star2 ); }; template< class T > struct ExteriorDerivative0Form { static void build( const Mesh& mesh, SparseMatrix& d0 ); }; template< class T > struct ExteriorDerivative1Form { static void build( const Mesh& mesh, SparseMatrix& d1 ); }; } #include "DiscreteExteriorCalculus.inl" #endif ================================================ FILE: Flatten/include/Edge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Edge.h // ----------------------------------------------------------------------------- // // Edge stores attributes associated with a mesh edge. The iterator he points // to one of its two associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_EDGE_H #define DDG_EDGE_H #include "Types.h" namespace DDG { class Edge { public: HalfEdgeIter he; // points to one of the two halfedges associated with this edge int index; // unique integer ID in the range 0, ..., nEdges-1 Edge() : index(0) { } }; } #endif ================================================ FILE: Flatten/include/Face.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Face.h // ----------------------------------------------------------------------------- // // Face stores attributes associated with a mesh edge. The iterator he points // to one of its associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_FACE_H #define DDG_FACE_H #include "Types.h" namespace DDG { class Face { public: HalfEdgeIter he; // points to one of the halfedges associated with this face int index; // unique integer ID in the range 0, ..., nFaces-1 Face() : index(0) { } bool isBoundary( void ) const; // returns true if this face corresponds to a // boundary loop; false otherwise double area( void ) const; // returns the triangle area Vector normal( void ) const; // returns the unit normal associated with this face; normal // orientation is determined by the circulation order of halfedges Vector circumcenter( void ) const; // returns triangle circumcenter Vector barycenter( void ) const; // returns triangle barycenter }; } #endif ================================================ FILE: Flatten/include/HalfEdge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- HalfEdge.h // ----------------------------------------------------------------------------- // // HalfEdge is used to define mesh connectivity. (See the documentation for a // more in-depth discussion of the halfedge data structure.) // #ifndef DDG_HALFEDGE_H #define DDG_HALFEDGE_H #include "Vector.h" #include "Types.h" namespace DDG { class HalfEdge { public: HalfEdgeIter next; // points to the next halfedge around the current face HalfEdgeIter flip; // points to the other halfedge associated with this edge VertexIter vertex; // points to the vertex at the "tail" of this halfedge EdgeIter edge; // points to the edge associated with this halfedge FaceIter face; // points to the face containing this halfedge bool onBoundary; // true if this halfedge is contained in a boundary // loop; false otherwise Vector texcoord; // texture coordinates associated with the triangle corner at the // "tail" of this halfedge double cotan( void ) const; // returns the cotangent of the angle opposing this edge Vector rotatedEdge( void ) const; // returns oriented edge vector rotated by PI/2 around face normal // if onBoundary, then return nil }; } #endif ================================================ FILE: Flatten/include/Image.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Image.h // ----------------------------------------------------------------------------- // // Image represents a color bitmap image. A simple example might look like // // Image im; // im.read( "input.tga" ); // // modify image data via im(x,y) = ...; // im.write( "output.tga" ); // #ifndef DDG_IMAGE_H #define DDG_IMAGE_H #include #include namespace DDG { class Image { public: Image( int width = 0, int height = 0 ); // constructs image with specified width and height float& operator()( int x, int y ); const float& operator()( int x, int y ) const; // accesses pixel (x,y) float sample( float x, float y ) const; // samples image at (x,y) using bilinear filtering int width( void ) const; int height( void ) const; // returns image dimensions void read( const char* filename ); // loads an image file in Truevision TGA format // (must be RGB image with 24 or 32 bits per pixel) void write( const char* filename ) const; // writes an image file in Truevision TGA format // (RGB image with 24 bits per pixel) protected: void clamp( int& x, int& y ) const; // clamps coordinates to range [0,w-1] x [0,h-1] int w, h; // width and height std::vector pixels; // interleaved RGBA pixel data in range [0-1] }; } #endif ================================================ FILE: Flatten/include/LinearContext.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearContext.h // ----------------------------------------------------------------------------- // // LinearContext is the global solver context needed to interface with the // SuiteSparse library. It is essentially a wrapper around cholmod_common. A // single static instance of LinearContext is declared in LinearContext.cpp and // is shared by all instances of DenseMatrix, SparseMatrix, and LinearSystem. // In other words, you shouldn't have to instantiate LinearContext yourself // unless you're doing something really fancy! // #ifndef DDG_LINEARSOLVERCONTEXT #define DDG_LINEARSOLVERCONTEXT #include namespace DDG { class LinearContext { public: LinearContext( void ); // constructor ~LinearContext( void ); // destructor operator cholmod_common*( void ); // allows LinearContext to be treated as a cholmod_common* protected: cholmod_common context; }; } #endif ================================================ FILE: Flatten/include/LinearEquation.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearEquation.h // ----------------------------------------------------------------------------- // // LinearEquation represents an equation with an arbitrary linear polynomial on // both the left- and right-hand side. It is primarily used while building a // LinearSystem. For convenience, operator== is overloaded so that the user // can construct a LinearEquation by writing something that looks much like the // usual mathematical syntax for a linear equation. For example, // // LinearEquation eqn = ( x + 2*y == 3*z ); // // builds the linear equation x + 2y = 3z. // #ifndef DDG_LINEAREQUATION_H #define DDG_LINEAREQUATION_H #include "LinearPolynomial.h" namespace DDG { class LinearEquation { public: LinearPolynomial lhs; // left-hand side LinearPolynomial rhs; // right-hand side }; LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ); // constructs a linear equation with the specified left- and right-hand side } #endif ================================================ FILE: Flatten/include/LinearPolynomial.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearPolynomial.h // ----------------------------------------------------------------------------- // // LinearPolynomial represents an affine function of the form // // f(x1,x2,...,xn) = c1 x1 + c2 x2 + ... + cn xn + d // // where the xi are real-valued variables with real coefficients ci, and d is // a real constant. The variables and their coefficients are represented using // instances of the Variable class. LinearPolynomial implements all the usual // algebraic operations on affine functions, as well as type conversions from // more elementary types (scalars, single variables, etc.). // // Importantly, variables used in a LinearPolynomial should *not* be deallocated // while the polynomial is still in use -- LinearPolynomial stores only a // reference to these variables so that the solution to a linear system can be // automatically copied back into the variables. // #ifndef DDG_LINEARPOLYNOMIAL_H #define DDG_LINEARPOLYNOMIAL_H #include #include #include "Variable.h" namespace DDG { class LinearPolynomial { public: LinearPolynomial( void ); // constructs the zero function LinearPolynomial( double c ); // constructs the constant function with value c LinearPolynomial( Variable& v ); // constructs a function with a single variable v const LinearPolynomial& operator=( double c ); // assigns the constant function with value c const LinearPolynomial& operator=( Variable& v ); // assigns a function with a single variable v void operator+=( double c ); void operator-=( double c ); void operator*=( double c ); void operator/=( double c ); // adds, subtract, multiplies, or divides by a constant void operator+=( Variable& v ); void operator-=( Variable& v ); // increments or decrements by a single variable v void operator+=( const LinearPolynomial& p ); void operator-=( const LinearPolynomial& p ); // increments or decrements by an affine function LinearPolynomial operator-( void ) const; // returns the additive inverse (i.e., negation) double evaluate( void ) const; // evaluates the function using the current values of its variables std::map linearTerms; // list of linear terms double constantTerm; // constant term }; LinearPolynomial operator+( double c, Variable& v ); // sum LinearPolynomial operator+( Variable& v, double c ); // sum LinearPolynomial operator-( double c, Variable& v ); // difference LinearPolynomial operator-( Variable& v, double c ); // difference LinearPolynomial operator*( double c, Variable& v ); // product LinearPolynomial operator*( Variable& v, double c ); // product LinearPolynomial operator/( Variable& v, double c ); // quotient // algebraic operations between single variables and constants LinearPolynomial operator+( Variable& v1, Variable& v2 ); // sum LinearPolynomial operator-( Variable& v1, Variable& v2 ); // difference // algebraic operations between pairs of variables LinearPolynomial operator+( const LinearPolynomial& p, double c ); // sum LinearPolynomial operator+( double c, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, double c ); // difference LinearPolynomial operator-( double c, const LinearPolynomial& p ); // difference LinearPolynomial operator*( const LinearPolynomial& p, double c ); // product LinearPolynomial operator*( double c, const LinearPolynomial& p ); // product LinearPolynomial operator/( const LinearPolynomial& p, double c ); // quotient // algebraic operations between polynomials and constants LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ); // sum LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ); // difference LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ); // difference // algebraic operations between polynomials and single variables LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ); // sum LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ); // difference // algebraic operations between pairs of polynomials std::ostream& operator<<( std::ostream& os, const LinearPolynomial& p ); // prints the symbolic representation of a polynomial (all variables must be named) } #endif ================================================ FILE: Flatten/include/LinearSystem.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearSystem.h // ----------------------------------------------------------------------------- // // LinearSystem represents a system of linear equations expressed in terms of // instances of the Variable class. The main idea is to make it easy to // construct and solve linear systems without explicitly think about variable // indices, matrix layout, etc. (This kind of abstraction is particularly // useful for debugging and rapid prototyping.) See the documentation for // examples of building linear and solving systems. // // Importantly, any variable used by a LinearSystem should not be deallocated // while the system is still in use, because the method LinearSystem::solve() // automatically copies the solution back into the variables used to define the // equations. (In the future variables may become reference-counted in order // to avoid this issue.) // // Note that LinearSystem::solve() uses a general-purpose linear solver (namely // the sparse QR factorization found in SuiteSparse) that is quite fast but may // not always be your best option. To improve performance you may want to // build the system explicitly using an instance of SparseMatrix and call a // more specialized solver. (In the future there may be options for specifying // that a LinearSystem is, e.g., symmetric and positive-definite.) // #ifndef DDG_LINEARSYSTEM_H #define DDG_LINEARSYSTEM_H #include #include "LinearEquation.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "Real.h" namespace DDG { class LinearSystem { public: void clear( void ); // removes all equations from the system void push_back( const LinearEquation& e ); // appends the equation e to the sytem void solve( void ); // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution std::vector equations; // the collection of equations defining the system protected: void convertEquations( void ); void indexVariables( void ); void buildSparseMatrix( void ); void buildRightHandSide( void ); void computeSolution( void ); int nEquations; int nVariables; std::vector currentEquations; std::map index; SparseMatrix A; DenseMatrix x; DenseMatrix b; }; } #endif ================================================ FILE: Flatten/include/Mesh.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Mesh.h // ----------------------------------------------------------------------------- // // Mesh represents a polygonal surface mesh using the halfedge data structure. // It is essentially a large collection of disjoint vertices, edges, and faces // that are ``glued together'' by halfedges which encode connectivity (see // the documentation for an illustration). By construction, the halfedge data // structure cannot represent nonorientable surfaces or meshes with nonmanifold // edges. // // Mesh elements are referenced using iterators -- common usage of these // iterators is to either traverse an entire vector of mesh elements: // // // visit all vertices // for( VertexIter i = vertices.begin(); i != vertices.end(); i++ ) // { // //... // } // // or to perform a local traversal over the neighborhood of some mesh element: // // // visit both halfedges of edge e // HalfEdgeIter he = e->he; // do // { // // ... // // he = he->flip; // } // while( he != e->he ); // // (See Types.h for an explicit definition of iterator types.) // // Meshes with boundary are handled by creating an additional face for each // boundary loop (the method Face::isBoundary() determines whether a given // face is a boundary loop). Isolated vertices (i.e., vertiecs not contained // in any edge or face) reference a dummy halfedge and can be checked via // the method Vertex::isIsolated(). // #ifndef DDG_MESH_H #define DDG_MESH_H #include #include #include "HalfEdge.h" #include "Vertex.h" #include "Edge.h" #include "Face.h" #include "SparseMatrix.h" namespace DDG { class Mesh { public: Mesh( void ); // constructs an empty mesh Mesh( const Mesh& mesh ); // constructs a copy of mesh const Mesh& operator=( const Mesh& mesh ); // copies mesh int read( const std::string& filename ); // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error int write( const std::string& filename ) const; // writes a mesh to a Wavefront OBJ file; return value is nonzero // only if there was an error bool reload( void ); // reloads a mesh from disk using the most recent input filename void normalize( void ); // centers around the origin and rescales to have unit radius double area( void ) const; // returns total mesh area double meanEdgeLength( void ) const; // returns mean edge lenght std::vector halfedges; std::vector vertices; std::vector edges; std::vector faces; std::vector boundaries; // storage for mesh elements protected: std::string inputFilename; void indexElements( void ); // assigns a unique, 0-based index to each mesh element }; } #endif ================================================ FILE: Flatten/include/MeshIO.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- MeshIO.h // ----------------------------------------------------------------------------- // // MeshIO handles input/output operations for Mesh objects. Currently the only // supported mesh format is Wavefront OBJ -- for a format specification see // // http://en.wikipedia.org/wiki/Wavefront_.obj_file // // Note that vertex normals and material properties are currently ignored. // #ifndef DDG_MESHIO_H #define DDG_MESHIO_H #include #include #include #include namespace DDG { class Mesh; class Index; class MeshData; class MeshIO { public: static int read( std::istream& in, Mesh& mesh ); // reads a mesh from a valid, open input stream in static void write( std::ostream& out, const Mesh& mesh ); // writes a mesh to a valid, open output stream out protected: static int readMeshData( std::istream& in, MeshData& data ); static void readPosition( std::stringstream& ss, MeshData& data ); static void readTexCoord( std::stringstream& ss, MeshData& data ); static void readNormal ( std::stringstream& ss, MeshData& data ); static void readFace ( std::stringstream& ss, MeshData& data ); static Index parseFaceIndex( const std::string& token ); static void preallocateMeshElements( const MeshData& data, Mesh& mesh ); static int buildMesh( const MeshData& data, Mesh& mesh ); static void checkIsolatedVertices( const Mesh& Mesh ); static void checkNonManifoldVertices( const Mesh& Mesh ); }; } #endif ================================================ FILE: Flatten/include/Quaternion.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Quaternion.h // ----------------------------------------------------------------------------- // // Quaternion represents an element of the quaternions, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // Hamilton product is expressed using the * operator: // // Quaternion p, q, r; // r = q * p; // // and conjugation is expressed using the method Quaternion::conj(): // // Quaternion q; // double normQSquared = -q.conj()*q; // // Individual components can be accessed in several ways: the real and imaginary // parts can be accessed using the methods Quaternion::re() and Quaternion::im(): // // Quaternion q; // double a = q.re(); // Vector b = q.im(); // // or by index: // // Quaternion q; // double a = q[0]; // double bi = q[1]; // double bj = q[2]; // double bk = q[3]; // #ifndef DDG_QUATERNION_H #define DDG_QUATERNION_H #include "Vector.h" #include "Complex.h" #include namespace DDG { class Quaternion { public: Quaternion( void ); // initializes all components to zero Quaternion( const Quaternion& q ); // initializes from existing quaternion Quaternion( double s, double vi, double vj, double vk ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s, const Vector& v ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s ); // initializes purely real quaternion with specified real (s) component (imaginary part is zero) Quaternion( const Vector& v ); // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) Quaternion( const Complex& z ); // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k const Quaternion& operator=( double s ); // assigns a purely real quaternion with real value s const Quaternion& operator=( const Vector& v ); // assigns a purely real quaternion with imaginary value v double& operator[]( int index ); // returns reference to the specified component (0-based indexing: r, i, j, k) const double& operator[]( int index ) const; // returns const reference to the specified component (0-based indexing: r, i, j, k) void toMatrix( double Q[4][4] ) const; // builds 4x4 matrix Q representing (left) quaternion multiplication double& re( void ); // returns reference to double part const double& re( void ) const; // returns const reference to double part Vector& im( void ); // returns reference to imaginary part const Vector& im( void ) const; // returns const reference to imaginary part Quaternion operator+( const Quaternion& q ) const; // addition Quaternion operator-( const Quaternion& q ) const; // subtraction Quaternion operator-( void ) const; // negation Quaternion operator*( double c ) const; // right scalar multiplication Quaternion operator/( double c ) const; // scalar division void operator+=( const Quaternion& q ); // addition / assignment void operator+=( double c ); // addition / assignment of pure real void operator-=( const Quaternion& q ); // subtraction / assignment void operator-=( double c ); // subtraction / assignment of pure real void operator*=( double c ); // scalar multiplication / assignment void operator/=( double c ); // scalar division / assignment Quaternion operator*( const Quaternion& q ) const; // Hamilton product void operator*=( const Quaternion& q ); // Hamilton product / assignment Quaternion conj( void ) const; // conjugation Quaternion inv( void ) const; // inverse double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Quaternion unit( void ) const; // returns unit quaternion void normalize( void ); // divides by Euclidean length protected: double s; // scalar (double) part Vector v; // vector (imaginary) part }; Quaternion operator*( double c, const Quaternion& q ); // left scalar multiplication std::ostream& operator<<( std::ostream& os, const Quaternion& q ); // prints components } #endif ================================================ FILE: Flatten/include/Real.h ================================================ #ifndef DDG_REAL_H #define DDG_REAL_H namespace DDG { class Real { public: Real( double x = 0. ); // constructs real number with value x operator double( void ) const; // type cast to double void operator+=( double x ); // increment void operator-=( double x ); // decrement void operator*=( double x ); // multiply void operator/=( double x ); // divide Real conj( void ) const; // simply returns the value (for compatibility w/ complex numbers) Real inv( void ) const; // returns inverse double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Real unit( void ) const; // returns number with unit norm and same sign protected: double value; // value }; } #endif ================================================ FILE: Flatten/include/Shader.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Shader.h // ----------------------------------------------------------------------------- // // Shader encapsulates the functionality of a shader program written in // the OpenGL Shader Language (GLSL). Basic usage is to read a collection // of source files to disk and enable the shader before making draw calls. // For instance, during initialization one might write // // Shader shader; // shader.loadVertex( "vertex.glsl" ); // shader.loadFragment( "fragment.glsl" ); // // and in the main draw routine write // // shader.enable(); // // draw some stuff // shader.disable(); // #ifndef DDG_SHADER_H #define DDG_SHADER_H #include #include namespace DDG { class Shader { public: Shader( void ); // constructor -- shader is initially invalid ~Shader( void ); // destructor void loadVertex( const char* filename ); // read vertex shader from GLSL source file void loadFragment( const char* filename ); // read fragment shader from GLSL source file void loadGeometry( const char* filename ); // read geometry shader from GLSL source file void enable( void ); // uses this shader for rendering void disable( void ) const; // uses the fixed-function pipeline for rendering operator GLuint( void ) const; // returns the ID of this shader program (for calls to OpenGL) protected: void load( GLenum shaderType, const char* filename, GLuint& shader ); bool readSource( const char* filename, std::string& source ); GLuint vertexShader; GLuint fragmentShader; GLuint geometryShader; GLuint program; bool linked; }; } #endif ================================================ FILE: Flatten/include/SparseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- SparseMatrix.h // ----------------------------------------------------------------------------- // // SparseMatrix represents an m by n (real or complex) matrix where only // nonzero entries are stored explicitly. This class is most commonly // used to represent the linear term in sparse linear systems (i.e., the matrix // part). // // A real or complex matrix is allocated via // // SparseMatrix A( m, n ); // SparseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // SparseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a SparseMatrix returns a // cholmod_sparse* which can be used by routines in SuiteSparse. For basic // operations, however, you should not need to access this pointer explicitly -- // see the solve() method below. // // Internally SparseMatrix stores nonzero entries in a heap data structure; the // amortized cost of insertion is therefore no worse than the sorting cost of // putting the matrix in compressed-column order. // #ifndef DDG_SPARSE_MATRIX_H #define DDG_SPARSE_MATRIX_H #include #include #include #include "Types.h" namespace DDG { template class SparseMatrix { public: SparseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix SparseMatrix( const SparseMatrix& B ); // copy constructor ~SparseMatrix( void ); // destructor const SparseMatrix& operator=( const SparseMatrix& B ); // copies B const SparseMatrix& operator=( cholmod_sparse* B ); // copies a cholmod_sparse* into a SparseMatrix; // takes responsibility for deallocating B void resize( int m, int n ); // clears and resizes to mxn matrix SparseMatrix transpose( void ) const; // returns the transpose of this matrix cholmod_sparse* to_cholmod( void ); // returns pointer to copy of matrix in compressed-column CHOLMOD format SparseMatrix operator*( const SparseMatrix& B ) const; // returns product of this matrix with sparse B DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with dense B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c void operator+=( const SparseMatrix& B ); // adds B to this matrix void operator-=( const SparseMatrix& B ); // subtracts B from this matrix SparseMatrix operator+( const SparseMatrix& B ) const; // returns sum of this matrix with B SparseMatrix operator-( const SparseMatrix& B ) const; // returns difference of this matrix with B int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val ); // sets all nonzero elements val SparseMatrix inverse( void ) const; // returns inverse -- for diagonal matrices only // (assertion occurs for non-diagonal matrices) static SparseMatrix identity( int N ); // returns the N x N identity matrix DenseMatrix full( void ) const; // converts to a dense matrix T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element (uses 0-based indexing) // TODO for legibility, replace w/ type where entries are named "row, // TODO col" instead of "first, second" (especially since we adopt the // TODO unorthodox convention of storing the column first) typedef std::pair EntryIndex; // convenience type for an entry index; note that we store column THEN // row, which makes it easier to build compressed column format typedef std::map EntryMap; typedef typename EntryMap::iterator iterator; typedef typename EntryMap::const_iterator const_iterator; // convenience types for storing and accessing entries iterator begin( void ); const_iterator begin( void ) const; iterator end( void ); const_iterator end( void ) const; // return iterators to first and last nonzero entries void shift( double c ); // adds c times the identity matrix to this matrix protected: int m, n; EntryMap data; cholmod_sparse* cData; void allocateSparse( void ); void setEntry( const_iterator e, int i, double* pr ); }; template SparseMatrix operator*( const SparseMatrix& A, const T& c ); // right scalar multiplication template SparseMatrix operator*( const T& c, const SparseMatrix& A ); // left scalar multiplication template SparseMatrix operator/( const SparseMatrix& A, const T& c ); // scalar division template std::ostream& operator << (std::ostream& os, const SparseMatrix& o); // prints entries template class SparseFactor { public: SparseFactor( void ); ~SparseFactor( void ); void build( SparseMatrix& A ); // factorizes positive-definite matrix A using CHOLMOD bool valid( void ) const; // returns true if the factor has been built; false otherwise cholmod_factor* to_cholmod( void ); // returns pointer to underlying cholmod_factor data structure protected: cholmod_factor *L; }; template void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse QR factorization template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse LU factorization template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ); // backsolves the prefactored positive definite sparse linear system LL'x = b template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ); // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ); // returns the max residual of the linear problem A x = b relative to the largest entry of the solution template double residual( const SparseMatrix& A, const DenseMatrix& x ); // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda B x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns /<(B-EE^T)x,x> } #include "SparseMatrix.inl" #endif ================================================ FILE: Flatten/include/Types.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Types.h // ----------------------------------------------------------------------------- // // This file contains forward declarations of common types and definitions of // convenience types for standard iterators. // #ifndef DDG_TYPES_H #define DDG_TYPES_H #include #include #include namespace DDG { // forward declarations class Camera; class Complex; class Edge; class Face; class HalfEdge; class Image; class LinearContext; class LinearEquation; class LinearPolynomial; class LinearSystem; class Mesh; class MeshIO; class Quaternion; class Real; class Shader; class Variable; class Vector; class Vertex; class Viewer; template class DenseMatrix; template class SparseMatrix; // convenience types for iterators typedef std::map::iterator TermIter; typedef std::map::const_iterator TermCIter; typedef std::vector::iterator PolyIter; typedef std::vector::const_iterator PolyCIter; typedef std::vector::iterator EqnIter; typedef std::vector::const_iterator EqnCIter; typedef std::map::iterator IndexIter; typedef std::map::const_iterator IndexCIter; typedef std::vector::iterator HalfEdgeIter; typedef std::vector::const_iterator HalfEdgeCIter; typedef std::vector::iterator VertexIter; typedef std::vector::const_iterator VertexCIter; typedef std::vector::iterator EdgeIter; typedef std::vector::const_iterator EdgeCIter; typedef std::vector::iterator FaceIter; typedef std::vector::const_iterator FaceCIter; } #endif ================================================ FILE: Flatten/include/Utility.h ================================================ #ifndef DDG_UTILITY_H #define DDG_UTILITY_H #include #include "Utility.h" #include "Complex.h" namespace DDG { inline double sqr( double x ) { return x*x; } inline double unitRand( void ) { const double rRandMax = 1. / (double) RAND_MAX; return rRandMax * (double) rand(); } inline double seconds( int t0, int t1 ) { return (double)(t1-t0) / (double) CLOCKS_PER_SEC; } } namespace DDGConstants { static DDG::Complex ii( 0., 1. ); } #endif ================================================ FILE: Flatten/include/Variable.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Variable.h // ----------------------------------------------------------------------------- // // Variable represents a variable that can be used to define a (linear or // nonlinear) system of equations. Its main feature is that it can be used // both as an abstract variable (e.g., when used to define an equation) but can // also store a definite numerical value. For instance, suppose we define a // linear polynomial // // Variable x, y; // LinearPolynomial p = x + 2*y; // // Now by assigning different numerical values to x and y, we can evaluate the // polynomial at different points: // // *x = 1; // *y = 2; // cout << p.evaluate() << endl; // // *x = 3; // *y = 4; // cout << p.evaluate() << endl; // // In general the dereference operator (*) accesses the numerical value. // Variables can also be named in order to aid with debugging. For instance, // // Variable x( "x" ); // Variable y( "y" ); // Polynomial p = x + 2*y; // cout << p << endl; // // will print out something like "x+2*y". // // The "fixed" flag in a variable refers to whether it is held constant while // solving a system of equations -- see the documentation for further discussion. // #ifndef DDG_VARIABLE_H #define DDG_VARIABLE_H #include namespace DDG { class Variable { public: Variable( double value = 0., bool fixed = false ); // initialize a variable which has value zero and is not fixed by default Variable( std::string name, double value = 0., bool fixed = false ); // initialize a named variable which has value zero and is not fixed by default double& operator*( void ); // returns a reference to the numerical value const double& operator*( void ) const; // returns a const reference to the numerical value std::string name; // names the variable (for display output) double value; // numerical value bool fixed; // true if a variable is held constant while solving a system of equations }; } #endif ================================================ FILE: Flatten/include/Vector.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vector.h // ----------------------------------------------------------------------------- // // Vector represents an element of Euclidean 3-space, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // inner product (i.e., scalar or dot product) is expressed using the global // method dot(): // // Vector u, v; // double cosTheta = dot( u, v ); // // and the cross product is expressed using the global method cross(): // // Vector u, v, w; // w = cross( u, v ); // // Individual components can be accessed in two ways: either directly via the // members x, y, and z: // // Vector v; // cout << v.x << endl; // cout << v.y << endl; // cout << v.z << endl; // // or by index: // // Vector v; // for( int i = 0; i < 3; i++ ) // { // cout << v[i] << endl; // } // #ifndef DDG_VECTOR_H #define DDG_VECTOR_H #include namespace DDG { class Vector { public: Vector(); // initializes all components to zero Vector( double x, double y, double z); // initializes with specified components Vector( const Vector& v ); // initializes from existing vector double& operator[] ( const int& index ); // returns reference to the specified component (0-based indexing: x, y, z) const double& operator[] ( const int& index ) const; // returns const reference to the specified component (0-based indexing: x, y, z) Vector operator+( const Vector& v ) const; // addition Vector operator-( const Vector& v ) const; // subtraction Vector operator-( void ) const; // negation Vector operator*( const double& c ) const; // right scalar multiplication Vector operator/( const double& c ) const; // scalar division void operator+=( const Vector& v ); // addition / assignment void operator-=( const Vector& v ); // subtraction / assignment void operator*=( const double& c ); // scalar multiplication / assignment void operator/=( const double& c ); // scalar division / assignment double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Vector unit( void ) const; // returns unit vector void normalize( void ); // divides by Euclidean length Vector abs( void ) const; // returns vector containing magnitude of each component double x, y, z; // components }; Vector operator* ( const double& c, const Vector& v ); // left scalar multiplication double dot( const Vector& u, const Vector& v ); // dot product (a.k.a. inner or scalar product) Vector cross( const Vector& u, const Vector& v ); // cross product std::ostream& operator << (std::ostream& os, const Vector& o); // prints components } #endif ================================================ FILE: Flatten/include/Vertex.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vertex.h // ----------------------------------------------------------------------------- // // Vertex stores attributes associated with a mesh edge. The iterator he // points to its "outgoing" halfedge. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_VERTEX_H #define DDG_VERTEX_H #include "Vector.h" #include "Types.h" namespace DDG { class Vertex { public: HalfEdgeIter he; // points to the "outgoing" halfedge Vector texture; // location of vertex in Euclidian 2-space Vector position; // location of vertex in Euclidean 3-space int index; // unique integer ID in the range 0, ..., nVertices-1 bool tag; // true if vertex is selected by the user; false otherwise Vertex() : index(0), tag(false) { } double area( void ) const; // returns the barycentric area associated with this vertex Vector normal( void ) const; // returns the vertex normal bool isIsolated( void ) const; // returns true if the vertex is not contained in any face or edge; false otherwise int valence( void ) const; // returns the number of incident faces / edges void toggleTag(); // toggle vertex tag }; } #endif ================================================ FILE: Flatten/include/Viewer.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Viewer.h // ----------------------------------------------------------------------------- // // Viewer provides a graphical user interface (GUI) for inspecting and // interacting with a Mesh object. Viewer methods are static in order // to make them compatible with GLUT callbacks. // #ifndef DDG_VIEWER_H #define DDG_VIEWER_H #include #include "Mesh.h" #include "Camera.h" #include "Shader.h" namespace DDG { class Viewer { public: static void init( void ); // displays the viewer until the program ends static Mesh mesh; // surface mesh visualized by Viewer protected: // init static void initGLUT( void ); static void initGLSL( void ); // GLUT callbacks static void display( void ); static void idle( void ); static void keyboard( unsigned char c, int x, int y ); static void special( int i, int x, int y ); static void mouse( int button, int state, int x, int y ); static void motion( int x, int y ); static void menu( int value ); static void view( int value ); // menu functions static void mProcess( void ); static void mResetMesh( void ); static void mWriteMesh( void ); static void mExit( void ); static void mWireframe( void ); static void mRender3D( void ); static void mQuasiConformal( void ); static void mZoomIn( void ); static void mZoomOut( void ); static void mScreenshot( void ); // unique identifiers for menus enum { menuProcess, menuResetMesh, menuWriteMesh, menuExit, menuWireframe, menuRender3D, menuQuasiConformal, menuZoomIn, menuZoomOut, menuScreenshot }; // draw routines static void setGL( void ); static void setLighting( void ); static void setMeshMaterial( void ); static void callDisplayList( void ); static void updateDisplayList( void ); static void drawScene( void ); static void drawPolygons( void ); static void drawWireframe( void ); static void drawVertices( void ); static void drawSelectedVertices( void ); static void drawIsolatedVertices( void ); static void pickVertex(int x, int y); static void storeViewerState( void ); static void restoreViewerState( void ); static int windowSize[2]; static bool renderQuasiConformal; // draw quasi conformal error static bool render3D; // draw 3D or 2D static bool renderWireframe; // draw wireframe static Camera camera; // keeps track of view state static GLuint surfaceDL; // display list for mesh static Shader shader; // shader used to determine appearance of surface }; // methods to viz quasi conformal error double faceQCDistortion( FaceCIter f ); Vector qcColor( double qc ); Vector HSV(double h, double s, double v); double quasiConformalDistortion( Vector p1, Vector p2, Vector p3, Vector q1, Vector q2, Vector q3 ); } #endif ================================================ FILE: Flatten/obj/.empty ================================================ ================================================ FILE: Flatten/shaders/fragment.glsl ================================================ uniform vec3 eye; uniform vec3 light; varying vec3 position; varying vec3 normal; float diffuse( vec3 N, vec3 L ) { return max( 0., dot( N, L )); } float specular( vec3 N, vec3 L, vec3 E ) { const float shininess = 8.; vec3 R = 2.*dot(L,N)*N - L; return pow( max( 0., dot( R, E )), shininess ); } float fresnel( vec3 N, vec3 E ) { const float sharpness = 10.; float NE = max( 0., dot( N, E )); return pow( sqrt( 1. - NE*NE ), sharpness ); } void main() { vec3 N = normalize( normal ); vec3 L = normalize( light - position ); vec3 E = normalize( eye - position ); vec3 R = 2.*dot(L,N)*N - L; vec3 one = vec3( 1., 1., 1. ); gl_FragColor.rgb = diffuse(N,L)*gl_Color.rgb + .5*specular(N,L,E)*one + .5*fresnel(N,E)*one; gl_FragColor.a = 1.; } ================================================ FILE: Flatten/shaders/vertex.glsl ================================================ varying vec3 position; varying vec3 normal; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_FrontColor = gl_Color; position = gl_Vertex.xyz; normal = gl_Normal.xyz; } ================================================ FILE: Flatten/src/Camera.cpp ================================================ #include #include #include using namespace std; #include "Camera.h" namespace DDG { Camera :: Camera( void ) : pClick( 1. ), pDrag( 1. ), pLast( 1. ), rLast( 1. ), momentum( 1. ), zoom( 1. ) {} Quaternion Camera :: clickToSphere( int x, int y ) { GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); int w = viewport[2]; int h = viewport[3]; Quaternion p( 0., 2. * (double) x / (double) w - 1., 2. * (double) y / (double) h - 1., 0. ); if( p.norm2() > 1. ) { p.normalize(); p.im().z = 0.; } else { p.im().z = sqrt( 1. - p.norm2() ); } return p; } Quaternion Camera :: currentRotation( void ) const { return ( pDrag * pClick.conj() ) * rLast; } void Camera :: setView( void ) const { Quaternion r = ( pDrag * pClick.conj() ) * rLast; double w = r[0]; double x = r[1]; double y = r[2]; double z = r[3]; GLdouble M[16] = { 1.-2.*y*y-2.*z*z, 2.*x*y+2.*w*z, 2.*x*z-2.*w*y, 0., 2.*x*y-2.*w*z, 1.-2.*x*x-2.*z*z, 2.*y*z+2.*w*x, 0., 2.*x*z+2.*w*y, 2.*y*z-2.*w*x, 1.-2.*x*x-2.*y*y, 0., 0., 0., 0., 1. }; glMatrixMode( GL_MODELVIEW ); glMultMatrixd( M ); } void Camera :: mouse( int button, int state, int x, int y ) { if( state == GLUT_DOWN ) { pClick = pDrag = pLast = clickToSphere( x, y ); momentum = 1.; } if( state == GLUT_UP ) { double timeSinceDrag = ( clock() - tLast ) / (double) CLOCKS_PER_SEC; if( timeSinceDrag < .1 ) { momentum = pDrag * pLast.conj(); momentum = ( .03 * momentum + .97 ).unit(); } else { momentum = 1.; } rLast = pDrag * pClick.conj() * rLast; pClick = pDrag = 1.; } } void Camera :: motion( int x, int y ) { tLast = clock(); pLast = pDrag; pDrag = clickToSphere( x, y ); } void Camera :: idle( void ) { // get time since last idle event static int t0 = clock(); int t1 = clock(); double dt = (t1-t0) / (double) CLOCKS_PER_SEC; rLast = momentum * rLast; momentum = ( (1.-.5*dt) * momentum + .5*dt ).unit(); zoom += vZoom*dt; vZoom *= max( 0., 1.-5.*dt ); t0 = t1; } void Camera :: zoomIn( void ) { vZoom -= 0.5; } void Camera :: zoomOut( void ) { vZoom += 0.5; } } ================================================ FILE: Flatten/src/Complex.cpp ================================================ #include "Complex.h" #include #include using namespace std; namespace DDG { Complex::Complex( double a, double b ) // constructs number a+bi : re( a ), im( b ) {} void Complex::operator+=( const Complex& z ) // add z { re += z.re; im += z.im; } void Complex::operator-=( const Complex& z ) // subtract z { re -= z.re; im -= z.im; } void Complex::operator*=( const Complex& z ) // Complex multiply by z { double a = re; double b = im; double c = z.re; double d = z.im; re = a*c-b*d; im = a*d+b*c; } void Complex::operator*=( double r ) // scalar multiply by r { re *= r; im *= r; } void Complex::operator/=( double r ) // scalar divide by r { re /= r; im /= r; } void Complex::operator/=( const Complex& z ) // scalar divide by r { *this *= z.inv(); } Complex Complex::operator-( void ) const { return Complex( -re, -im ); } Complex Complex::conj( void ) const // returns Complex conjugate { return Complex( re, -im ); } Complex Complex::inv( void ) const // returns inverse { return this->conj() / this->norm2(); } double Complex::arg( void ) const // returns argument { return atan2( im, re ); } double Complex::norm( void ) const // returns norm { return sqrt( re*re + im*im ); } double Complex::norm2( void ) const // returns norm squared { return re*re + im*im; } Complex Complex::unit( void ) const // returns complex number with unit norm and same modulus { return *this / this->norm(); } Complex Complex::exponential( void ) const // complex exponentiation { return exp( re ) * Complex( cos( im ), sin( im )); } Complex operator+( const Complex& z1, const Complex& z2 ) // binary addition { Complex z = z1; z += z2; return z; } Complex operator-( const Complex& z1, const Complex& z2 ) // binary subtraction { Complex z = z1; z -= z2; return z; } Complex operator*( const Complex& z1, const Complex& z2 ) // binary Complex multiplication { Complex z = z1; z *= z2; return z; } Complex operator*( const Complex& z, double r ) // right scalar multiplication { Complex zr = z; zr *= r; return zr; } Complex operator*( double r, const Complex& z ) // left scalar multiplication { return z*r; } Complex operator/( const Complex& z, double r ) // scalar division { Complex zr = z; zr /= r; return zr; } Complex operator/( const Complex& z1, const Complex& z2 ) // complex division { Complex z = z1; z /= z2; return z; } double dot( const Complex& z1, const Complex& z2 ) { return z1.re*z2.re + z1.im*z2.im; } double cross( const Complex& z1, const Complex& z2 ) { return z1.re*z2.im - z1.im*z2.re; } std::ostream& operator<<( std::ostream& os, const Complex& z ) // prints components { if( z.im > 0 ) { os << z.re << " + " << z.im << "i"; } else if( z.im < 0 ) { os << z.re << " - " << -z.im << "i"; } else { os << z.re; } return os; } } ================================================ FILE: Flatten/src/DenseMatrix.cpp ================================================ #include "DenseMatrix.h" namespace DDG { template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i] = data[i]; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_COMPLEX, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i*2+0] = data[i].re; x[i*2+1] = data[i].im; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { assert( nColumns() == 1 ); if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } cData = cholmod_l_allocate_dense( m*4, 1, m*4, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { x[i*4+k] = data[i][k]; } } return cData; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = x[i]; } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = Complex( x[i*2+0], x[i*2+1] ); } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); assert( B->ncol == 1 ); assert( B->nrow%4 == 0 ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow/4; n = 1; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m; i++ ) { data[i] = Quaternion( x[i*4+0], x[i*4+1], x[i*4+2], x[i*4+3] ); } return *this; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 3; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { double x = o(i,j); if( x == 0. ) { os << " 0"; for( int k = 0; k < p+6; k++ ) { os << " "; } } else if( x > 0. ) { os << " " << x << " "; } else { os << x << " "; } } os << "]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { Complex z = o(i,j); if( z.re == 0. ) { os << " 0"; for( int k = 0; k < p+5; k++ ) { os << " "; } } else if( z.re > 0. ) { os << " " << z.re; } else { os << z.re; } if( z.im == 0 ) { os << " "; } else if( z.im >= 0 ) { os << "+"; } else { os << "-"; } if( z.im == 0. ) { for( int k = 0; k < p+8; k++ ) { os << " "; } } else { os << abs( z.im ) << "i "; } } os << " ]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "["; for( int j = 0; j < o.nColumns(); j++ ) { Quaternion q = o(i,j); os << " " << q; } os << " ]" << endl; } return os; } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i] = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i].re = 2.*unitRand() - 1.; data[i].im = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { data[i][k] = 2.*unitRand() - 1.; } } } } ================================================ FILE: Flatten/src/DenseMatrix.inl ================================================ #include #include #include #include using namespace std; #include "DenseMatrix.h" #include "LinearContext.h" #include "Quaternion.h" #include "SparseMatrix.h" #include "Utility.h" namespace DDG { extern LinearContext context; template DenseMatrix :: DenseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) { data.resize( m*n ); zero(); } template DenseMatrix :: DenseMatrix( const DenseMatrix& A ) // copy constructor : cData( NULL ) { *this = A; } template DenseMatrix :: ~DenseMatrix( void ) // destructor { if( cData != NULL ) { cholmod_l_free_dense( &cData, context ); } } template DenseMatrix DenseMatrix :: transpose( void ) const { const DenseMatrix& A( *this ); DenseMatrix AT( n, m ); for( int i = 0; i < n; i++ ) for( int j = 0; j < m; j++ ) { AT(i,j) = A(j,i).conj(); } return AT; } template SparseMatrix DenseMatrix::sparse( void ) // converts to a sparse matrix { SparseMatrix B; B = cholmod_l_dense_to_sparse( this->to_cholmod(), true, context ); return B; } template DenseMatrix DenseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); DenseMatrix AB( A.nRows(), B.nColumns() ); for( int i = 0; i < A.nRows(); i++ ) for( int j = 0; j < B.nColumns(); j++ ) for( int k = 0; k < A.nColumns(); k++ ) { AB( i, j ) += A( i, k ) * B( k, j ); } return AB; } template void DenseMatrix :: operator*=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c; } } template void DenseMatrix :: operator/=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c.inv(); } } template DenseMatrix DenseMatrix :: operator+( const DenseMatrix& B ) const // returns sum of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) + B(i,j); } return C; } template void DenseMatrix :: operator+=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) += B(i,j); } } template DenseMatrix DenseMatrix :: operator-( const DenseMatrix& B ) const // returns difference of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) - B(i,j); } return C; } template void DenseMatrix :: operator-=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) -= B(i,j); } } template DenseMatrix operator*( const T& c, const DenseMatrix& A ) { DenseMatrix cA = A; cA *= c; return cA; } template DenseMatrix operator*( const DenseMatrix& A, double c ) { return c*A; } template DenseMatrix operator/( const DenseMatrix& A, double c ) { DenseMatrix Ac = A; Ac /= c; return Ac; } template const DenseMatrix& DenseMatrix :: operator=( const DenseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template int DenseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int DenseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int DenseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void DenseMatrix :: zero( const T& val ) // sets all elements to val { for( int i = 0; i < m*n; i++ ) { data[i] = val; } } template double DenseMatrix :: norm( NormType type ) const { double r = 0.; if( type == lInfinity ) { for( int i = 0; i < m*n; i++ ) { r = max( r, data[i].norm() ); } } else if( type == lOne ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm(); } } else if( type == lTwo ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm2(); } r = sqrt( r ); } return r; } template void DenseMatrix :: normalize( void ) // divides by l2 norm { *this /= norm( lTwo ); } template T& DenseMatrix :: operator()( int row, int col ) { return data[row+m*col]; } template T DenseMatrix :: operator()( int row, int col ) const { return data[row+m*col]; } template T& DenseMatrix :: operator()( int index ) { return data[index]; } template T DenseMatrix :: operator()( int index ) const { return data[index]; } template T DenseMatrix::sum( void ) const // returns the sum of all entries { T total( 0., 0. ); for( int i = 0; i < m*n; i++ ) { total += data[i]; } return total; } template void DenseMatrix :: removeMean( void ) { T mean = 0.; int N = m*n; for( int i = 0; i < N; i++ ) { mean += data[i]; } mean /= (double) N; for( int i = 0; i < N; i++ ) { data[i] -= mean; } } template T dot( const DenseMatrix& x, const DenseMatrix& y ) // returns Euclidean inner product of x and y { return ( x.transpose() * y )(0); } template DenseMatrix DenseMatrix::operator-( void ) const // returns additive inverse of this matrix { const DenseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { B( i, j ) = -A( i, j ); } return B; } template T inner( const DenseMatrix& x, const DenseMatrix& y ) // standard inner product { T sum = 0.; assert( x.nRows() == y.nRows() && x.nColumns() == y.nColumns() ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * y(i); } return sum; } template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ) // inner product with respect a diagonal inner // product B represented as a dense vector { T sum = 0.; assert( x.nRows() == y.nRows() && x.nRows() == B.nRows() && x.nColumns() == 1 && B.nColumns() == 1 && y.nColumns() == 1 ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * B(i) * y(i); } return sum; } } ================================================ FILE: Flatten/src/DiscreteExteriorCalculus.inl ================================================ #include "DiscreteExteriorCalculus.h" namespace DDG { template void HodgeStar0Form :: build( const Mesh& mesh, SparseMatrix& star0 ) // builds a diagonal matrix mapping primal discrete 0-forms // to dual discrete 2-forms { int nV = mesh.vertices.size(); star0 = SparseMatrix( nV, nV ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { int i = v->index; star0( i, i ) = v->area(); } } template void HodgeStar1Form :: build( const Mesh& mesh, SparseMatrix& star1 ) // builds a diagonal matrix mapping primal discrete 1-forms // to dual discrete 1-forms { int nE = mesh.edges.size(); star1 = SparseMatrix( nE, nE ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // get the cotangents of the two angles opposite this edge double cotAlpha = e->he->cotan(); double cotBeta = e->he->flip->cotan(); int i = e->index; star1( i, i ) = ( cotAlpha + cotBeta ) / 2.; } } template void HodgeStar2Form :: build( const Mesh& mesh, SparseMatrix& star2 ) // builds a diagonal matrix mapping primal discrete 2-forms // to dual discrete 2-forms { int nF = mesh.faces.size(); star2 = SparseMatrix( nF, nF ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { int i = f->index; star2( i, i ) = 1. / f->area(); } } template< class T > void ExteriorDerivative0Form :: build( const Mesh& mesh, SparseMatrix& d0 ) { int nV = mesh.vertices.size(); int nE = mesh.edges.size(); d0 = SparseMatrix( nE, nV ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // the row index is the index of the edge int r = e->index; // the column indices are the indices of the two // edge vertices -- orientation is determined by // the orientation of the edge's first half edge int ci = e->he->vertex->index; int cj = e->he->flip->vertex->index; d0( r, ci ) = -1.; d0( r, cj ) = 1.; } } template< class T > void ExteriorDerivative1Form :: build( const Mesh& mesh, SparseMatrix& d1 ) { int nE = mesh.edges.size(); int nF = mesh.faces.size(); d1 = SparseMatrix( nF, nE ); // visit each face for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { // the row index is the index of the face int r = f->index; // visit all edges of this face HalfEdgeCIter he = f->he; do { // the column index is the index of the current edge int c = he->edge->index; // relative orientation is determined by checking if // the current half edge is the first half edge of its // corresponding edge double s = ( he->edge->he == he ? 1. : -1. ); // set the entry for this edge d1( r, c ) = s; he = he->next; } while( he != f->he ); } } } ================================================ FILE: Flatten/src/Edge.cpp ================================================ #include "Edge.h" #include "Mesh.h" namespace DDG { } ================================================ FILE: Flatten/src/Face.cpp ================================================ #include "Face.h" #include "Mesh.h" #include "Vector.h" namespace DDG { double Face::area( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).norm() / 2.; } Vector Face::normal( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).unit(); } bool Face::isBoundary( void ) const { return he->onBoundary; } Vector Face :: circumcenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector n = he->rotatedEdge(); double h = 0.5*he->cotan(); return 0.5*(p0+p1) + h*n; } Vector Face :: barycenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return (p0 + p1 + p2)/3.; } } ================================================ FILE: Flatten/src/HalfEdge.cpp ================================================ #include "HalfEdge.h" #include "Mesh.h" namespace DDG { double HalfEdge :: cotan( void ) const { if( onBoundary ) return 0.0; Vector p0 = next->next->vertex->position; Vector p1 = vertex->position; Vector p2 = next->vertex->position; Vector u = p1-p0; Vector v = p2-p0; return dot( u, v ) / cross( u, v ).norm(); } Vector HalfEdge :: rotatedEdge( void ) const { if( onBoundary ) return Vector(); Vector n = face->normal(); Vector p0 = vertex->position; Vector p1 = flip->vertex->position; return cross( n, p1-p0 ); } } ================================================ FILE: Flatten/src/Image.cpp ================================================ #include #include #include #include using namespace std; #include "Image.h" namespace DDG { Image :: Image( int width, int height ) : w( width ), h( height ), pixels( w*h*3 ) {} float& Image :: operator()( int x, int y ) // accesses pixel (x,y) { return pixels[ x + y*w ]; } const float& Image :: operator()( int x, int y ) const // accesses pixel (x,y) { return pixels[ x + y*w ]; } float Image :: sample( float x, float y ) const // samples image at (x,y) using bilinear filtering { const Image& I( *this ); float ax = x - floor( x ); float ay = y - floor( y ); float bx = 1. - ax; float by = 1. - ay; int x0 = (int) floor( x ); int y0 = (int) floor( y ); int x1 = x0 + 1; int y1 = y0 + 1; clamp( x0, y0 ); clamp( x1, y1 ); return by * ( bx * I(x0,y0) + ax * I(x1,y0) ) + ay * ( bx * I(x0,y1) + ax * I(x1,y1) ) ; } int Image :: width( void ) const // returns image width { return w; } int Image :: height( void ) const // returns image height { return h; } class TGAHeader // header format for Truevision TGA images { public: char idFieldSize; char colorMapType; char dataTypeCode; short colorMapOrigin; short colorMapLength; char colorMapEntrySize; short xOrigin; short yOrigin; short width; short height; char bitsPerPixel; char imageSpecification; }; void Image :: read( const char* filename ) // loads an image file in Truevision TGA format // (must be uncompressed RGB image with 24 or 32 bits per pixel) { ifstream in( filename, ios_base::binary ); if( !in.is_open() ) { cerr << "Error: could not open file " << filename << " for input!" << endl; exit( 1 ); } // read header TGAHeader header; in.read( (char*) &(header.idFieldSize), 1 ); in.read( (char*) &(header.colorMapType), 1 ); in.read( (char*) &(header.dataTypeCode), 1 ); in.read( (char*) &(header.colorMapOrigin), 2 ); in.read( (char*) &(header.colorMapLength), 2 ); in.read( (char*) &(header.colorMapEntrySize), 1 ); in.read( (char*) &(header.xOrigin), 2 ); in.read( (char*) &(header.yOrigin), 2 ); in.read( (char*) &(header.width), 2 ); in.read( (char*) &(header.height), 2 ); in.read( (char*) &(header.bitsPerPixel), 1 ); in.read( (char*) &(header.imageSpecification), 1 ); w = header.width; h = header.height; // validate data type const char uncompressedRGB = 2; if( header.dataTypeCode != uncompressedRGB || ( header.bitsPerPixel != 24 && header.bitsPerPixel != 32 )) { cerr << "Error: input must be uncompressed RGB image with 24 or 32 bits per pixel." << endl; exit( 1 ); } // read identification field (unused) vector idField( header.idFieldSize ); in.read( &idField[0], header.idFieldSize ); // read color map data (unused) if( header.colorMapType == 1 ) { int bytesPerEntry = header.colorMapEntrySize / 8; int colorMapSize = header.colorMapLength * bytesPerEntry; vector colorMapData( colorMapSize ); in.read( &colorMapData[0], colorMapSize ); } // read pixel data int n = w*h*header.bitsPerPixel/8; vector pixelData( n ); in.read( (char*) &pixelData[0], n ); // convert pixel data to floating point pixels.resize( n ); for( int i = 0; i < n; i++ ) { pixels[i] = (double) pixelData[i] / 255.; } } void Image :: write( const char* filename ) const // writes an image file in Truevision TGA format // (uncompressed RGB image with 24 bits per pixel) { ofstream out( filename, ios_base::binary ); if( !out.is_open() ) { cerr << "Error: could not open file " << filename << " for output!" << endl; exit( 1 ); } TGAHeader header; header.idFieldSize = 0; header.colorMapType = 0; header.dataTypeCode = 2; header.colorMapOrigin = 0; header.colorMapLength = 0; header.colorMapEntrySize = 0; header.xOrigin = 0; header.yOrigin = 0; header.width = w; header.height = h; header.bitsPerPixel = 24; header.imageSpecification = 0; // write header out.write( (char*) &(header.idFieldSize), 1 ); out.write( (char*) &(header.colorMapType), 1 ); out.write( (char*) &(header.dataTypeCode), 1 ); out.write( (char*) &(header.colorMapOrigin), 2 ); out.write( (char*) &(header.colorMapLength), 2 ); out.write( (char*) &(header.colorMapEntrySize), 1 ); out.write( (char*) &(header.xOrigin), 2 ); out.write( (char*) &(header.yOrigin), 2 ); out.write( (char*) &(header.width), 2 ); out.write( (char*) &(header.height), 2 ); out.write( (char*) &(header.bitsPerPixel), 1 ); out.write( (char*) &(header.imageSpecification), 1 ); // convert pixel data from floating point vector pixelData( w*h*3 ); for( int i = 0; i < w*h*3; i++ ) { pixelData[i] = (unsigned char)( pixels[i] * 255. ); } // write pixel data out.write( (char*) &pixelData[0], w*h*3 ); } void Image :: clamp( int& x, int& y ) const // clamps coordinates to range [0,w-1] x [0,h-1] { x = max( 0, min( w-1, x )); y = max( 0, min( h-1, y )); } } ================================================ FILE: Flatten/src/LinearContext.cpp ================================================ #include "LinearContext.h" namespace DDG { // global context for linear solvers LinearContext context; LinearContext :: LinearContext( void ) // constructor { cholmod_l_start( &context ); } LinearContext :: ~LinearContext( void ) // destructor { cholmod_l_finish( &context ); } LinearContext :: operator cholmod_common*( void ) // allows LinearContext to be treated as a cholmod_common* { return &context; } } ================================================ FILE: Flatten/src/LinearEquation.cpp ================================================ #include "LinearEquation.h" namespace DDG { LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ) // constructs a linear equation with the specified left- and right-hand side { LinearEquation eqn; eqn.lhs = lhs; eqn.rhs = rhs; return eqn; } } ================================================ FILE: Flatten/src/LinearPolynomial.cpp ================================================ #include using namespace std; #include "LinearPolynomial.h" #include "Types.h" namespace DDG { LinearPolynomial :: LinearPolynomial( void ) : constantTerm( 0. ) {} LinearPolynomial :: LinearPolynomial( double c ) { *this = c; } LinearPolynomial :: LinearPolynomial( Variable& v ) { *this = v; } const LinearPolynomial& LinearPolynomial :: operator=( double c ) { linearTerms.clear(); constantTerm = c; return *this; } const LinearPolynomial& LinearPolynomial :: operator=( Variable& v ) { linearTerms.clear(); linearTerms[ &v ] = 1.; constantTerm = 0.; return *this; } void LinearPolynomial::operator+=( double c ) { constantTerm += c; } void LinearPolynomial::operator-=( double c ) { constantTerm -= c; } void LinearPolynomial::operator*=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second *= c; } constantTerm *= c; } void LinearPolynomial::operator/=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second /= c; } constantTerm /= c; } void LinearPolynomial::operator+=( Variable& v ) { LinearPolynomial p( v ); *this += p; } void LinearPolynomial::operator-=( Variable& v ) { LinearPolynomial p( v ); *this -= p; } void LinearPolynomial::operator+=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second += i->second; } else { linearTerms[i->first] = i->second; } } constantTerm += e.constantTerm; } void LinearPolynomial::operator-=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second -= i->second; } else { linearTerms[i->first] = -i->second; } } constantTerm -= e.constantTerm; } LinearPolynomial LinearPolynomial::operator-( void ) const { LinearPolynomial p = *this; for( TermIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { i->second = -i->second; } p.constantTerm = -p.constantTerm; return p; } double LinearPolynomial::evaluate( void ) const { double value = constantTerm; for( TermCIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { value += i->second * i->first->value; } return value; } LinearPolynomial operator+( double c, Variable& v ) { return c + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, double c ) { return LinearPolynomial(v) + c; } LinearPolynomial operator-( double c, Variable& v ) { return c - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, double c ) { return LinearPolynomial(v) - c; } LinearPolynomial operator*( double c, Variable& v ) { return LinearPolynomial(v) * c; } LinearPolynomial operator*( Variable& v, double c ) { return LinearPolynomial(v) * c; } LinearPolynomial operator/( Variable& v, double c ) { return LinearPolynomial(v) / c; } LinearPolynomial operator+( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) + LinearPolynomial(v2); } LinearPolynomial operator-( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) - LinearPolynomial(v2); } LinearPolynomial operator+( const LinearPolynomial& p, double c ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator+( double c, const LinearPolynomial& p ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, double c ) { LinearPolynomial difference = p; difference -= c; return difference; } LinearPolynomial operator-( double c, const LinearPolynomial& p ) { LinearPolynomial difference = -p; difference += c; return difference; } LinearPolynomial operator*( const LinearPolynomial& p, double c ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator*( double c, const LinearPolynomial& p ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator/( const LinearPolynomial& p, double c ) { LinearPolynomial quotient = p; quotient /= c; return quotient; } LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ) { return p + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) + p; } LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ) { return p - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) - p; } LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial sum = p; sum += q; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial difference = p; difference -= q; return difference; } ostream& operator<<( ostream& os, const LinearPolynomial& p ) { for( TermCIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { os << i->second << "*" << i->first->name << " + "; } os << p.constantTerm; return os; } } ================================================ FILE: Flatten/src/LinearSystem.cpp ================================================ #include using namespace std; #include #include "LinearSystem.h" #include "LinearContext.h" #include "Types.h" namespace DDG { extern LinearContext context; void LinearSystem::clear( void ) // removes all equations from the system { equations.clear(); } void LinearSystem::push_back( const LinearEquation& e ) // appends the equation e to the sytem { equations.push_back( e ); } void LinearSystem::solve( void ) // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution { convertEquations(); indexVariables(); buildSparseMatrix(); buildRightHandSide(); computeSolution(); } void LinearSystem::convertEquations( void ) // converts each equation to its polynomial representation { currentEquations.clear(); for( EqnIter eqn = equations.begin(); eqn != equations.end(); eqn ++ ) { // move right-hand side to left-hand side LinearPolynomial p = eqn->lhs - eqn->rhs; // convert fixed variables to constants LinearPolynomial q( p.constantTerm ); for( TermIter t = p.linearTerms.begin(); t != p.linearTerms.end(); t ++ ) { const double& coefficient( t->second ); Variable& variable( *(t->first) ); // skip zeros if( coefficient == 0. ) continue; if( t->first->fixed ) { q += coefficient * variable.value; } else { q += coefficient * variable; } } if( q.linearTerms.size() > 0 ) { currentEquations.push_back( q ); } } nEquations = currentEquations.size(); } void LinearSystem::indexVariables( void ) // assign a unique index to each variable remaining in one of the polynomials { index.clear(); nVariables = 0; for( PolyCIter p = currentEquations.begin(); p != currentEquations.end(); p ++ ) { for( TermCIter t = p->linearTerms.begin(); t != p->linearTerms.end(); t ++ ) { IndexIter j = index.find( t->first ); // if we haven't seen this variable // before, assign it a unique index if( j == index.end() ) { index[ t->first ] = nVariables; nVariables++; } } } } void LinearSystem::buildSparseMatrix( void ) // build the sparse matrix representation of our current system { A = SparseMatrix( nEquations, nVariables ); for( int i = 0; i < nEquations; i++ ) { for( TermCIter t = currentEquations[i].linearTerms.begin(); t != currentEquations[i].linearTerms.end(); t ++ ) { int j = index[ t->first ]; A(i,j) = t->second; } } } void LinearSystem::buildRightHandSide( void ) // build the data vector for our current system { b = DenseMatrix( nEquations, 1 ); for( int i = 0; i < nEquations; i++ ) { b(i) = -currentEquations[i].constantTerm; } } void LinearSystem::computeSolution( void ) { // solve linear system Ax=b x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); // put solution values in variables for( IndexIter i = index.begin(); i != index.end(); i ++ ) { i->first->value = x( i->second ); } } } ================================================ FILE: Flatten/src/Mesh.cpp ================================================ #include #include #include "Mesh.h" #include "MeshIO.h" #include "DiscreteExteriorCalculus.h" using namespace std; namespace DDG { Mesh :: Mesh( void ) {} Mesh :: Mesh( const Mesh& mesh ) { *this = mesh; } class HalfEdgeIterCompare { public: bool operator()( const HalfEdgeIter& i, const HalfEdgeIter& j ) const { return &*i < &*j; } }; class HalfEdgeCIterCompare { public: bool operator()( const HalfEdgeCIter& i, const HalfEdgeCIter& j ) const { return &*i < &*j; } }; class VertexIterCompare { public: bool operator()( const VertexIter& i, const VertexIter& j ) const { return &*i < &*j; } }; class VertexCIterCompare { public: bool operator()( const VertexCIter& i, const VertexCIter& j ) const { return &*i < &*j; } }; class FaceIterCompare { public: bool operator()( const FaceIter& i, const FaceIter& j ) const { return &*i < &*j; } }; class FaceCIterCompare { public: bool operator()( const FaceCIter& i, const FaceCIter& j ) const { return &*i < &*j; } }; class EdgeIterCompare { public: bool operator()( const EdgeIter& i, const EdgeIter& j ) const { return &*i < &*j; } }; class EdgeCIterCompare { public: bool operator()( const EdgeCIter& i, const EdgeCIter& j ) const { return &*i < &*j; } }; const Mesh& Mesh :: operator=( const Mesh& mesh ) { map< HalfEdgeCIter, HalfEdgeIter, HalfEdgeCIterCompare > halfedgeOldToNew; map< VertexCIter, VertexIter, VertexCIterCompare > vertexOldToNew; map< EdgeCIter, EdgeIter, EdgeCIterCompare > edgeOldToNew; map< FaceCIter, FaceIter, FaceCIterCompare > faceOldToNew; // copy geometry from the original mesh and create a // map from pointers in the original mesh to // those in the new mesh halfedges.clear(); for( HalfEdgeCIter he = mesh.halfedges.begin(); he != mesh.halfedges.end(); he++ ) halfedgeOldToNew[ he ] = halfedges.insert( halfedges.end(), *he ); vertices.clear(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) vertexOldToNew[ v ] = vertices.insert( vertices.end(), *v ); edges.clear(); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e++ ) edgeOldToNew[ e ] = edges.insert( edges.end(), *e ); faces.clear(); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) faceOldToNew[ f ] = faces.insert( faces.end(), *f ); // "search and replace" old pointers with new ones for( HalfEdgeIter he = halfedges.begin(); he != halfedges.end(); he++ ) { he->next = halfedgeOldToNew[ he->next ]; he->flip = halfedgeOldToNew[ he->flip ]; he->vertex = vertexOldToNew[ he->vertex ]; he->edge = edgeOldToNew[ he->edge ]; he->face = faceOldToNew[ he->face ]; } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) v->he = halfedgeOldToNew[ v->he ]; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) e->he = halfedgeOldToNew[ e->he ]; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) f->he = halfedgeOldToNew[ f->he ]; return *this; } int Mesh::read( const string& filename ) { inputFilename = filename; ifstream in( filename.c_str() ); if( !in.is_open() ) { cerr << "Error reading from mesh file " << filename << endl; return 1; } int rval; if( !( rval = MeshIO::read( in, *this ))) { indexElements(); normalize(); for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) v->texture = v->position; } return rval; } int Mesh::write( const string& filename ) const // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error { ofstream out( filename.c_str() ); if( !out.is_open() ) { cerr << "Error writing to mesh file " << filename << endl; return 1; } MeshIO::write( out, *this ); return 0; } bool Mesh::reload( void ) { return read( inputFilename ); } void Mesh::normalize( void ) { // compute center of mass Vector c( 0., 0., 0. ); for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { c += v->position; } c /= (double) vertices.size(); // translate to origin for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position -= c; } // rescale such that the mesh sits inside the unit ball double rMax = 0.; for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { rMax = max( rMax, v->position.norm() ); } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position /= rMax; } } void Mesh::indexElements( void ) { int nV = 0; for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->index = nV; nV++; } int nE = 0; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) { e->index = nE; nE++; } int nF = 0; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) { f->index = nF; nF++; } } double Mesh::area( void ) const { double sum = 0.0; for( FaceCIter f = faces.begin(); f != faces.end(); f ++ ) { sum += f->area(); } return sum; } double Mesh::meanEdgeLength( void ) const { double sum = 0; for( EdgeCIter e = edges.begin(); e != edges.end(); e ++) { VertexIter v0 = e->he->vertex; VertexIter v1 = e->he->flip->vertex; sum += (v0->position - v1->position).norm(); } return sum / edges.size(); } } ================================================ FILE: Flatten/src/MeshIO.cpp ================================================ #include #include #include #include #include "MeshIO.h" #include "Mesh.h" using namespace std; namespace DDG { class Index { public: Index( void ) {} Index( int p, int t, int n ) : position( p ), texcoord( t ), normal( n ) {} bool operator<( const Index& i ) const { if( position < i.position ) return true; if( position > i.position ) return false; if( texcoord < i.texcoord ) return true; if( texcoord > i.texcoord ) return false; if( normal < i.normal ) return true; if( normal > i.normal ) return false; return false; } int position; int texcoord; int normal; }; class MeshData { public: std::vector positions; std::vector texcoords; std::vector normals; std::vector< std::vector< Index > > indices; }; int MeshIO :: read( istream& in, Mesh& mesh ) // reads a mesh from a valid, open input stream in { MeshData data; if( readMeshData( in, data )) { return 1; } if( buildMesh( data, mesh )) { return 1; } return 0; } void MeshIO :: write( ostream& out, const Mesh& mesh ) // writes a mesh to a valid, open output stream out { int currentIndex = 1; map vertexIndex; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) { out << "v " << v->position.x << " " << v->position.y << " " << v->position.z << endl; vertexIndex[ v ] = currentIndex; currentIndex++; } for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeIter he = f->he; for( int j = 0; j < 3; j++ ) { out << "vt " << he->texcoord.x << " " << he->texcoord.y << endl; he = he->next; } } for( size_t i = 0; i < mesh.faces.size(); i++ ) { const Face& f( mesh.faces[i] ); HalfEdgeIter he = f.he; out << "f "; int j = 0; do { out << vertexIndex[ he->vertex ] << "/" << 1+(i*3+j) << " "; he = he->next; j++; } while( he != f.he ); out << endl; } } int MeshIO :: readMeshData( istream& in, MeshData& data ) { string line; while( getline( in, line )) { stringstream ss( line ); string token; ss >> token; if( token == "v" ) { readPosition( ss, data ); continue; } // vertex if( token == "vt" ) { readTexCoord( ss, data ); continue; } // texture coordinate if( token == "vn" ) { readNormal ( ss, data ); continue; } // vertex normal if( token == "f" ) { readFace ( ss, data ); continue; } // face if( token[0] == '#' ) continue; // comment if( token == "o" ) continue; // object name if( token == "g" ) continue; // group name if( token == "s" ) continue; // smoothing group if( token == "mtllib" ) continue; // material library if( token == "usemtl" ) continue; // material if( token == "" ) continue; // empty string cerr << "Error: does not appear to be a valid Wavefront OBJ file!" << endl; cerr << "(Offending line: " << line << ")" << endl; return 1; } return 0; } void MeshIO :: preallocateMeshElements( const MeshData& data, Mesh& mesh ) { // count the number of edges set< pair > edges; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { for( unsigned int I = 0; I < f->size(); I++ ) { int J = (I+1) % f->size(); int i = (*f)[I].position; int j = (*f)[J].position; if( i > j ) swap( i, j ); edges.insert( pair( i, j )); } } int nV = data.positions.size(); int nE = edges.size(); int nF = data.indices.size(); int nHE = 2*nE; int chi = nV - nE + nF; int nB = max( 0, 2 - chi ); // (conservative approximation of number of boundary cycles) mesh.halfedges.clear(); mesh.vertices.clear(); mesh.edges.clear(); mesh.faces.clear(); mesh.boundaries.clear(); mesh.halfedges.reserve( nHE ); mesh.vertices.reserve( nV ); mesh.edges.reserve( nE ); mesh.faces.reserve( nF ); mesh.boundaries.reserve( nB ); } extern vector isolated; // all isolated vertices point to isolated.begin() int MeshIO :: buildMesh( const MeshData& data, Mesh& mesh ) { map< pair< int, int >, int > edgeCount; map< pair< int, int >, HalfEdgeIter > existingHalfEdges; map< int, VertexIter > indexToVertex; map< HalfEdgeIter, bool > hasFlipEdge; preallocateMeshElements( data, mesh ); // allocate a vertex for each position in the data and construct // a map from vertex indices to vertex pointers for( unsigned int i = 0; i < data.positions.size(); i++ ) { VertexIter newVertex = mesh.vertices.insert( mesh.vertices.end(), Vertex() ); newVertex->position = data.positions[ i ]; newVertex->he = isolated.begin(); indexToVertex[ i ] = newVertex; } // insert each face into the mesh int faceIndex = 0; bool degenerateFaces = false; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { int N = f->size(); // print an error if the face is degenerate if( N < 3 ) { cerr << "Error: face " << faceIndex << " is degenerate (fewer than three vertices)!" << endl; degenerateFaces = true; continue; } // create a new face FaceIter newFace = mesh.faces.insert( mesh.faces.end(), Face()); // create a new half edge for each edge of the current face vector< HalfEdgeIter > hes( N ); for( int i = 0; i < N; i++ ) { hes[ i ] = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); } // initialize these new halfedges for( int i = 0; i < N; i++ ) { // the current halfedge goes from vertex a to vertex b int a = (*f)[ i ].position; int b = (*f)[ (i+1) % N ].position; // set current halfedge's attributes hes[ i ]->next = hes[ (i+1) % N ]; hes[ i ]->vertex = indexToVertex[ a ]; int t = (*f)[i].texcoord; if( t >= 0 ) hes[ i ]->texcoord = data.texcoords[ t ]; else hes[ i ]->texcoord = Vector( 0., 0., 0. ); hes[ i ]->onBoundary = false; // keep track of which halfedges have flip edges defined (for detecting boundaries) hasFlipEdge[ hes[ i ]] = false; // point vertex a at the current halfedge indexToVertex[ a ]->he = hes[ i ]; // point the new face and this half edge to each-other hes[ i ]->face = newFace; newFace->he = hes[ i ]; // if we've created an edge between a and b in the past, it is the // flip edge of the current halfedge if( a > b ) swap( a, b ); if( existingHalfEdges.find( pair( a, b )) != existingHalfEdges.end()) { hes[ i ]->flip = existingHalfEdges[ pair( a, b ) ]; hes[ i ]->flip->flip = hes[ i ]; hes[ i ]->edge = hes[ i ]->flip->edge; hasFlipEdge[ hes[ i ]] = true; hasFlipEdge[ hes[ i ]->flip ] = true; } else // otherwise, create an edge connected to the current halfedge { hes[ i ]->edge = mesh.edges.insert( mesh.edges.end(), Edge()); hes[ i ]->edge->he = hes[i]; edgeCount[ pair( a, b ) ] = 0; } // record the fact that we've created a halfedge from a to b existingHalfEdges[ pair( a, b ) ] = hes[ i ]; // check for nonmanifold edges edgeCount[ pair( a, b ) ]++; if( edgeCount[ pair( a, b ) ] > 2 ) { cerr << "Error: edge (" << a << ", " << b << ") is nonmanifold (more than two faces sharing a single edge)!" << endl; return 1; } } faceIndex++; } // give up now if there were degenerate faces if( degenerateFaces ) { return 1; } // insert extra faces for each boundary cycle for( HalfEdgeIter currentHE = mesh.halfedges.begin(); currentHE != mesh.halfedges.end(); currentHE ++ ) { // if we find a halfedge with no flip edge defined, create // a new face and link it to the corresponding boundary cycle if( !hasFlipEdge[ currentHE ] ) { // create a new face FaceIter newBoundary = mesh.boundaries.insert( mesh.boundaries.end(), Face()); // walk along this boundary cycle vector boundaryCycle; HalfEdgeIter he = currentHE; do { // create a new halfedge on the boundary face HalfEdgeIter newHE = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); // mark only the halfedge on the boundary face as being on the boundary newHE->onBoundary = true; // link the current halfedge in the cycle to its new flip edge he->flip = newHE; // grab the next halfedge along the boundary by finding // the next halfedge around the current vertex that doesn't // have a flip edge defined HalfEdgeIter nextHE = he->next; while( hasFlipEdge[ nextHE ] ) { nextHE = nextHE->flip->next; } // set attributes for the flip edge (we'll set ->next below) newHE->flip = he; newHE->vertex = nextHE->vertex; newHE->edge = he->edge; newHE->face = newBoundary; newHE->texcoord = nextHE->texcoord; // point the new face to this half edge newBoundary->he = newHE; // keep track of all the new halfedges in the boundary cycle boundaryCycle.push_back( newHE ); // continue to walk along the cycle he = nextHE; } while( he != currentHE ); // link together the cycle of boundary halfedges unsigned int N = boundaryCycle.size(); for( unsigned int i = 0; i < N; i++ ) { boundaryCycle[ i ]->next = boundaryCycle[ (i+N-1)%N ]; hasFlipEdge[ boundaryCycle[i] ] = true; hasFlipEdge[ boundaryCycle[i]->flip ] = true; } } } // print a warning if the input has any non-terminal defects checkIsolatedVertices( mesh ); checkNonManifoldVertices( mesh ); return 0; } void MeshIO :: readPosition( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.positions.push_back( Vector( x, y, z )); } void MeshIO :: readTexCoord( stringstream& ss, MeshData& data ) { double u, v; ss >> u >> v; data.texcoords.push_back( Vector( u, v, 0. )); } void MeshIO :: readNormal( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.normals.push_back( Vector( x, y, z )); } void MeshIO :: readFace( stringstream& ss, MeshData &data ) { vector faceIndices; string token; while( ss >> token ) { faceIndices.push_back( parseFaceIndex( token )); } data.indices.push_back( faceIndices ); } Index MeshIO :: parseFaceIndex( const string& token ) { // parse indices of the form // // p/[t]/[n] // // where p is an index into positions, t is an index into // texcoords, n is an index into normals, and [.] indicates // that an index is optional stringstream in( token ); string indexstring; int indices[3] = { -1, -1, -1 }; int i = 0; while( getline( in, indexstring, '/' )) { stringstream ss( indexstring ); ss >> indices[i++]; } // decrement since indices in OBJ files are 1-based return Index( indices[0]-1, indices[1]-1, indices[2]-1 ); } void MeshIO :: checkIsolatedVertices( const Mesh& mesh ) { // print a warning if the mesh has any isolated vertices int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { cerr << "Warning: vertex " << vertexIndex << " is isolated (not contained in any face)." << endl; } vertexIndex++; } } void MeshIO :: checkNonManifoldVertices( const Mesh& mesh ) { map nIncidentFaces; for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } for( FaceCIter f = mesh.boundaries.begin(); f != mesh.boundaries.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( nIncidentFaces[v] != v->valence() ) { cerr << "Warning: vertex " << vertexIndex << " is nonmanifold." << endl; } vertexIndex++; } } } ================================================ FILE: Flatten/src/Quaternion.cpp ================================================ #include #include using namespace std; #include "Quaternion.h" namespace DDG { // CONSTRUCTORS ---------------------------------------------------------- Quaternion :: Quaternion( void ) // initializes all components to zero : s( 0. ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Quaternion& q ) // initializes from existing quaternion : s( q.s ), v( q.v ) {} Quaternion :: Quaternion( double s_, double vi, double vj, double vk ) // initializes with specified double (s) and imaginary (v) components : s( s_ ), v( vi, vj, vk ) {} Quaternion :: Quaternion( double s_, const Vector& v_ ) // initializes with specified double(s) and imaginary (v) components : s( s_ ), v( v_ ) {} Quaternion :: Quaternion( double s_ ) // initializes purely real quaternion with specified real (s) component (imaginary part is zero) : s( s_ ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Vector& v_ ) // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) : s( 0. ), v( v_ ) {} Quaternion :: Quaternion( const Complex& z ) // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k : s( z.re ), v( z.im, 0., 0. ) {} // ASSIGNMENT OPERATORS -------------------------------------------------- const Quaternion& Quaternion :: operator=( double _s ) // assigns a purely real quaternion with real value s { s = _s; v = Vector( 0., 0., 0. ); return *this; } const Quaternion& Quaternion :: operator=( const Vector& _v ) // assigns a purely real quaternion with imaginary value v { s = 0.; v = _v; return *this; } // ACCESSORS ------------------------------------------------------------- double& Quaternion::operator[]( int index ) // returns reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } const double& Quaternion::operator[]( int index ) const // returns const reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } void Quaternion::toMatrix( double Q[4][4] ) const // returns 4x4 matrix representation { Q[0][0] = s; Q[0][1] = -v.x; Q[0][2] = -v.y; Q[0][3] = -v.z; Q[1][0] = v.x; Q[1][1] = s; Q[1][2] = -v.z; Q[1][3] = v.y; Q[2][0] = v.y; Q[2][1] = v.z; Q[2][2] = s; Q[2][3] = -v.x; Q[3][0] = v.z; Q[3][1] = -v.y; Q[3][2] = v.x; Q[3][3] = s; } double& Quaternion::re( void ) // returns reference to double part { return s; } const double& Quaternion::re( void ) const // returns const reference to double part { return s; } Vector& Quaternion::im( void ) // returns reference to imaginary part { return v; } const Vector& Quaternion::im( void ) const // returns const reference to imaginary part { return v; } // VECTOR SPACE OPERATIONS ----------------------------------------------- Quaternion Quaternion::operator+( const Quaternion& q ) const // addition { return Quaternion( s+q.s, v+q.v ); } Quaternion Quaternion::operator-( const Quaternion& q ) const // subtraction { return Quaternion( s-q.s, v-q.v ); } Quaternion Quaternion::operator-( void ) const // negation { return Quaternion( -s, -v ); } Quaternion Quaternion::operator*( double c ) const // scalar multiplication { return Quaternion( s*c, v*c ); } Quaternion operator*( double c, const Quaternion& q ) // scalar multiplication { return q*c; } Quaternion Quaternion::operator/( double c ) const // scalar division { return Quaternion( s/c, v/c ); } void Quaternion::operator+=( const Quaternion& q ) // addition / assignment { s += q.s; v += q.v; } void Quaternion::operator+=( double c ) // addition / assignment of pure real { s += c; } void Quaternion::operator-=( const Quaternion& q ) // subtraction / assignment { s -= q.s; v -= q.v; } void Quaternion::operator-=( double c ) // subtraction / assignment of pure real { s -= c; } void Quaternion::operator*=( double c ) // scalar multiplication / assignment { s *= c; v *= c; } void Quaternion::operator/=( double c ) // scalar division / assignment { s /= c; v /= c; } // ALGEBRAIC OPERATIONS -------------------------------------------------- Quaternion Quaternion::operator*( const Quaternion& q ) const // Hamilton product { const double& s1( s ); const double& s2( q.s ); const Vector& v1( v ); const Vector& v2( q.v ); return Quaternion( s1*s2 - dot(v1,v2), s1*v2 + s2*v1 + cross(v1,v2) ); } void Quaternion::operator*=( const Quaternion& q ) // Hamilton product / assignment { *this = ( *this * q ); } Quaternion Quaternion::conj( void ) const // conjugation { return Quaternion( s, -v ); } Quaternion Quaternion::inv( void ) const { return ( this->conj() ) / this->norm2(); } // NORMS ----------------------------------------------------------------- double Quaternion::norm( void ) const // returns Euclidean length { return sqrt( s*s + v.x*v.x + v.y*v.y + v.z*v.z ); } double Quaternion::norm2( void ) const // returns Euclidean length squared { return s*s + dot(v,v); } Quaternion Quaternion::unit( void ) const // returns unit quaternion { return *this / norm(); } void Quaternion::normalize( void ) // divides by Euclidean length { *this /= norm(); } // GEOMETRIC OPERATIONS -------------------------------------------------- Quaternion slerp( const Quaternion& q0, const Quaternion& q1, double t ) // spherical-linear interpolation { // interpolate length double m0 = q0.norm(); double m1 = q1.norm(); double m = (1-t)*m0 + t*m1; // interpolate direction Quaternion p0 = q0 / m0; Quaternion p1 = q1 / m1; double theta = acos(( p0.conj()*p1 ).re() ); Quaternion p = ( sin((1-t)*theta)*p0 + sin(t*theta)*p1 )/sin(theta); return m*p; } // I/O ------------------------------------------------------------------------- std::ostream& operator<<( std::ostream& os, const Quaternion& q ) // prints components { os << "( " << q.re() << ", " << q.im() << " )"; return os; } } ================================================ FILE: Flatten/src/Real.cpp ================================================ #include "Real.h" #include namespace DDG { Real :: Real( double x ) // constructs real number with value x : value( x ) {} Real :: operator double( void ) const // type cast to double { return value; } void Real :: operator+=( double x ) // increment { value += x; } void Real :: operator-=( double x ) // decrement { value -= x; } void Real :: operator*=( double x ) // multiply { value *= x; } void Real :: operator/=( double x ) // divide { value /= x; } Real Real :: conj( void ) const // simply returns the value (for compatibility w/ complex numbers) { return value; } Real Real :: inv( void ) const // returns inverse { return 1. / value; } double Real :: norm( void ) const // returns norm { return fabs( value ); } double Real :: norm2( void ) const // returns norm squared { return value * value; } Real Real :: unit( void ) const // returns number with unit norm and same sign { return value / norm(); } } ================================================ FILE: Flatten/src/Shader.cpp ================================================ #include "Shader.h" #include #include using namespace std; namespace DDG { Shader::Shader( void ) // constructor -- shader is initially invalid : vertexShader( 0 ), fragmentShader( 0 ), geometryShader( 0 ), program( 0 ), linked( false ) {} Shader::~Shader( void ) { if( program ) glDeleteProgram( program ); if( vertexShader ) glDeleteShader( vertexShader ); if( fragmentShader ) glDeleteShader( fragmentShader ); if( geometryShader ) glDeleteShader( geometryShader ); } void Shader::loadVertex( const char* filename ) { load( GL_VERTEX_SHADER, filename, vertexShader ); } void Shader::loadFragment( const char* filename ) { load( GL_FRAGMENT_SHADER, filename, fragmentShader ); } void Shader::loadGeometry( const char* filename ) { #ifdef GL_GEOMETRY_SHADER_EXT load( GL_GEOMETRY_SHADER_EXT, filename, geometryShader ); #else cerr << "Error: geometry shaders not supported!" << endl; #endif } void Shader::enable( void ) { if( !linked ) { glLinkProgram( program ); linked = true; } glUseProgram( program ); } void Shader::disable( void ) const { glUseProgram( 0 ); } Shader::operator GLuint( void ) const { return program; } void Shader::load( GLenum shaderType, const char* filename, GLuint& shader ) // read vertex shader from GLSL source file, compile, and attach to program { string source; if( !readSource( filename, source )) { return; } if( program == 0 ) { program = glCreateProgram(); } if( shader != 0 ) { glDetachShader( program, shader ); } shader = glCreateShader( shaderType ); const char* source_c_str = source.c_str(); glShaderSource( shader, 1, &(source_c_str), NULL ); glCompileShader( shader ); GLint compileStatus; glGetShaderiv( shader, GL_COMPILE_STATUS, &compileStatus ); if( compileStatus == GL_TRUE ) { glAttachShader( program, shader ); linked = false; } else { GLsizei maxLength = 0; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength ); if( maxLength > 0 ) { GLchar* infoLog = new char[ maxLength ]; GLsizei length; glGetShaderInfoLog( shader, maxLength, &length, infoLog ); cerr << "GLSL Error: " << infoLog << endl; delete[] infoLog; } } } bool Shader::readSource( const char* filename, std::string& source ) // reads GLSL source file into a string { source = ""; ifstream in( filename ); if( !in.is_open() ) { cerr << "Error: could not open shader file "; cerr << filename; cerr << " for input!" << endl; return false; } string line; while( getline( in, line )) { source += line; } return true; } } ================================================ FILE: Flatten/src/SparseMatrix.cpp ================================================ #include "SparseMatrix.h" namespace DDG { template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = pr[k]; } } return *this; } template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = Complex( pr[k*2+0], pr[k*2+1] ); } } return *this; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ) { SparseMatrix A( m*4, n*4 ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; const Quaternion& q( e->second ); A(i*4+0,j*4+0) = q[0]; A(i*4+0,j*4+1) = -q[1]; A(i*4+0,j*4+2) = -q[2]; A(i*4+0,j*4+3) = -q[3]; A(i*4+1,j*4+0) = q[1]; A(i*4+1,j*4+1) = q[0]; A(i*4+1,j*4+2) = -q[3]; A(i*4+1,j*4+3) = q[2]; A(i*4+2,j*4+0) = q[2]; A(i*4+2,j*4+1) = q[3]; A(i*4+2,j*4+2) = q[0]; A(i*4+2,j*4+3) = -q[1]; A(i*4+3,j*4+0) = q[3]; A(i*4+3,j*4+1) = -q[2]; A(i*4+3,j*4+2) = q[1]; A(i*4+3,j*4+3) = q[0]; } if( cData != NULL ) { cholmod_l_free_sparse( &cData, context ); } cData = cholmod_l_copy_sparse( A.to_cholmod(), context ); return cData; } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_COMPLEX, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m*4, n*4, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i] = e->second; } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i*2+0] = e->second.re; pr[i*2+1] = e->second.im; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR< complex >( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (complex)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (quaternion)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4]/4 << "\n"; } template <> void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_zl_symbolic( n, n, Ap, Ai, Ax, NULL, &Symbolic, NULL, NULL ); umfpack_zl_numeric( Ap, Ai, Ax, NULL, Symbolic, &Numeric, NULL, NULL ); umfpack_zl_solve( UMFPACK_A, Ap, Ai, Ax, NULL, (double*) &x(0), NULL, (double*) &b(0), NULL, Numeric, NULL, NULL ); umfpack_zl_free_symbolic( &Symbolic ); umfpack_zl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } } ================================================ FILE: Flatten/src/SparseMatrix.inl ================================================ #include #include #include #include #include using namespace std; #include #include #include "Real.h" #include "Complex.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "LinearContext.h" #include "Utility.h" namespace DDG { extern LinearContext context; const int maxEigIter = 20; // number of iterations used to solve eigenvalue problems template SparseMatrix :: SparseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) {} template SparseMatrix :: SparseMatrix( const SparseMatrix& B ) // copy constructor : cData( NULL ) { *this = B; } template SparseMatrix :: ~SparseMatrix( void ) // destructor { if( cData ) { cholmod_l_free_sparse( &cData, context ); } } template const SparseMatrix& SparseMatrix :: operator=( const SparseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template SparseMatrix SparseMatrix :: transpose( void ) const { SparseMatrix AT( n, m ); for( const_iterator e = begin(); e != end(); e++ ) { int i = e->first.second; int j = e->first.first; T Aij = e->second; AT(j,i) = Aij.conj(); } return AT; } template SparseMatrix SparseMatrix :: operator*( const SparseMatrix& B ) const // returns product of this matrix with sparse B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // collect nonzeros in each row vector< vector< int > > Bcol( B.nRows() ); vector< vector< T > > Bval( B.nRows() ); for( const_iterator e = B.begin(); e != B.end(); e ++ ) { int row = e->first.second; int col = e->first.first; T val = e->second; Bcol[ row ].push_back( col ); Bval[ row ].push_back( val ); } // multiply C = A*B SparseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( size_t n = 0; n < Bcol[j].size(); n++ ) { int k = Bcol[j][n]; C( i, k ) += e->second * Bval[j][n]; } } return C; } template DenseMatrix SparseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with dense B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // multiply C = A*B DenseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( int k = 0; k < B.nColumns(); k++ ) { C( i, k ) += e->second * B( j, k ); } } return C; } template void SparseMatrix :: operator*=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second *= c; } } template void SparseMatrix :: operator/=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second /= c; } } template void SparseMatrix :: operator+=( const SparseMatrix& B ) // adds B to this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) += Bij; } } template void SparseMatrix :: operator-=( const SparseMatrix& B ) // subtracts B from this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) -= Bij; } } template SparseMatrix SparseMatrix :: operator+( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C += B; return C; } template SparseMatrix SparseMatrix :: operator-( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C -= B; return C; } template SparseMatrix operator*( const T& c, const SparseMatrix& A ) { SparseMatrix cA = A; for( typename SparseMatrix::iterator e = cA.begin(); e != cA.end(); e++ ) { e->second = c * e->second; } return cA; } template SparseMatrix operator*( const SparseMatrix& A, const T& c ) { SparseMatrix Ac = A; Ac *= c; return Ac; } template SparseMatrix operator/( const SparseMatrix& A, T c ) { SparseMatrix Ac = A; Ac /= c; return Ac; } template void SparseMatrix :: resize( int m_, int n_ ) { m = m_; n = n_; data.clear(); } template int SparseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int SparseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int SparseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void SparseMatrix :: zero( const T& val ) // sets all nonzero elements val { for( iterator i = begin(); i != end(); i++ ) { i->second = val; } } template SparseMatrix SparseMatrix :: inverse( void ) const // returns inverse -- for diagonal matrices only { assert( m == n ); // matrix must be square const SparseMatrix& A( *this ); SparseMatrix Ainv( m, m ); for( const_iterator e = begin(); e != end(); e++ ) { int r = e->first.second; int c = e->first.first; assert( r == c ); // matrix must be diagonal Ainv( r, c ) = A( r, c ).inv(); } return Ainv; } template SparseMatrix SparseMatrix :: identity( int N ) { SparseMatrix I( N, N ); for( int i = 0; i < N; i++ ) { I( i, i ) = 1.; } return I; } template DenseMatrix SparseMatrix :: full( void ) const // converts to a dense matrix { const int maxSize = 1048576; if( m*n > maxSize ) { cerr << "Error: refusing to convert sparse to dense (too big!)" << "\n"; exit( 1 ); } const SparseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < m; j++ ) { B( i, j ) = A( i, j ); } return B; } template cholmod_sparse* SparseMatrix :: to_cholmod( void ) { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } allocateSparse(); // build compressed matrix (note that EntryMap stores entries in column-major order) double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; int i = 0; int j = -1; for( const_iterator e = begin(); e != end(); e ++ ) { int c = e->first.first; if( c != j ) { for( int k = j+1; k <= c; k++ ) { jc[k] = i; } j = c; } ir[i] = e->first.second; setEntry( e, i, pr ); i++; } for( int k = j+1; k <= n; k++ ) { jc[k] = i; } return cData; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ); template T& SparseMatrix :: operator()( int row, int col ) { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { data[ index ] = T( 0. ); } return data[ index ]; } template T SparseMatrix :: operator()( int row, int col ) const { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { return T( 0. ); } return entry->second; } template typename SparseMatrix::iterator SparseMatrix :: begin( void ) { return data.begin(); } template typename SparseMatrix::const_iterator SparseMatrix :: begin( void ) const { return data.begin(); } template typename SparseMatrix::iterator SparseMatrix :: end( void ) { return data.end(); } template typename SparseMatrix::const_iterator SparseMatrix :: end( void ) const { return data.end(); } template void SparseMatrix :: shift( double c ) // adds c times the identity matrix to this matrix { assert( m == n ); SparseMatrix& A( *this ); for( int i = 0; i < m; i++ ) { A( i, i ) += c; } } template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_dl_symbolic( n, n, Ap, Ai, Ax, &Symbolic, NULL, NULL ); umfpack_dl_numeric( Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL ); umfpack_dl_solve( UMFPACK_A, Ap, Ai, Ax, (double*) &x(0), (double*) &b(0), Numeric, NULL, NULL ); umfpack_dl_free_symbolic( &Symbolic ); umfpack_dl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; cholmod_factor* L = cholmod_l_analyze( Ac, context ); cholmod_l_factorize( Ac, L, context ); x = cholmod_l_solve( CHOLMOD_A, L, b.to_cholmod(), context ); if( L ) cholmod_l_free_factor( &L, context ); int t1 = clock(); cout << "[chol] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[chol] max residual: " << residual( A, x, b ) << "\n"; } template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ) // backsolves the prefactored positive definite sparse linear system LL'x = b { x = cholmod_l_solve( CHOLMOD_A, L.to_cholmod(), b.to_cholmod(), context ); } template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess { int t0 = clock(); for( int iter = 0; iter < maxEigIter; iter++ ) { solve( A, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess { // TODO use a symmetric matrix decomposition instead of QR int t0 = clock(); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= dot( e, B*e ).norm(); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; solve( A, x, x ); x -= dot( x, Be ).conj()*e; x /= dot( x, B*x ).norm(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); for( int iter = 0; iter < maxEigIter; iter++ ) { backsolvePositiveDefinite( L, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= sqrt( dot( e, B*e ).norm() ); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; backsolvePositiveDefinite( L, x, x ); x -= dot( x, Be ).conj()*e; x /= sqrt( dot( x, B*x ).norm() ); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ) // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess { int iter; int t0 = clock(); DenseMatrix ET = E.transpose(); SparseFactor L; L.build( A ); for( iter = 0; iter < maxEigIter; iter++ ) { x = B*x - E*(ET*x); backsolvePositiveDefinite( L, x, x ); x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, E, x ) << "\n"; } template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ) // returns the max residual of the linear problem A x = b relative to the largest entry of the solution { return ( A*x - b ).norm() / b.norm(); } template double residual( const SparseMatrix& A, const DenseMatrix& x ) // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, x ); return (A*x-lambda*x).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, x ); return (A*x-lambda*(B*x)).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, E, x ); return (A*x-lambda*(B*x-E*(E.transpose()*x))).norm() / x.norm(); } template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*x)(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x))(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns /<(B-EE^T)x,x> { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x-E*(E.transpose()*x)))(0).inv(); } template std::ostream& operator<<( std::ostream& os, const SparseMatrix& o) { os.precision( 3 ); for( typename SparseMatrix::const_iterator e = o.begin(); e != o.end(); e ++ ) { int row = e->first.second; int col = e->first.first; os << "( " << row << ", " << col << " ): " << e->second << "\n"; } return os; } template SparseFactor :: SparseFactor( void ) : L( NULL ) {} template SparseFactor :: ~SparseFactor( void ) { if( L ) { cholmod_l_free_factor( &L, context ); } } template void SparseFactor :: build( SparseMatrix& A ) { if( L ) { cholmod_l_free_factor( &L, context ); L = NULL; } int t0, t1; cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; t0 = clock(); L = cholmod_l_analyze( Ac, context ); t1 = clock(); cerr << "analyze: " << seconds(t0,t1) << "s" << endl; t0 = clock(); cholmod_l_factorize( Ac, L, context ); t1 = clock(); cerr << "factorize: " << seconds(t0,t1) << "s" << endl; } template bool SparseFactor :: valid( void ) const { if( L == NULL ) { return false; } return true; } template cholmod_factor* SparseFactor :: to_cholmod( void ) { return L; } } ================================================ FILE: Flatten/src/Variable.cpp ================================================ #include "Variable.h" namespace DDG { Variable :: Variable( double value_, bool fixed_ ) // initialize a variable which has value zero and is not fixed by default : value( value_ ), fixed( fixed_ ) {} Variable :: Variable( std::string name_, double value_, bool fixed_ ) // initialize a named variable which has value zero and is not fixed by default : name( name_ ), value( value_ ), fixed( fixed_ ) {} double& Variable :: operator*( void ) // returns a reference to the numerical value { return value; } const double& Variable :: operator*( void ) const // returns a const reference to the numerical value { return value; } } ================================================ FILE: Flatten/src/Vector.cpp ================================================ #include #include "Vector.h" namespace DDG { Vector :: Vector( void ) : x( 0. ), y( 0. ), z( 0. ) {} Vector :: Vector( double x0, double y0, double z0 ) : x( x0 ), y( y0 ), z( z0 ) {} Vector :: Vector( const Vector& v ) : x( v.x ), y( v.y ), z( v.z ) {} double& Vector :: operator[]( const int& index ) { return ( &x )[ index ]; } const double& Vector :: operator[]( const int& index ) const { return ( &x )[ index ]; } Vector Vector :: operator+( const Vector& v ) const { return Vector( x + v.x, y + v.y, z + v.z ); } Vector Vector :: operator-( const Vector& v ) const { return Vector( x - v.x, y - v.y, z - v.z ); } Vector Vector :: operator-( void ) const { return Vector( -x, -y, -z ); } Vector Vector :: operator*( const double& c ) const { return Vector( x*c, y*c, z*c ); } Vector operator*( const double& c, const Vector& v ) { return v*c; } Vector Vector :: operator/( const double& c ) const { return (*this) * ( 1./c ); } void Vector :: operator+=( const Vector& v ) { x += v.x; y += v.y; z += v.z; } void Vector :: operator-=( const Vector& v ) { x -= v.x; y -= v.y; z -= v.z; } void Vector :: operator*=( const double& c ) { x *= c; y *= c; z *= c; } void Vector :: operator/=( const double& c ) { (*this) *= ( 1./c ); } double Vector :: norm( void ) const { return sqrt( norm2()); } double Vector :: norm2( void ) const { return dot( *this, *this ); } void Vector :: normalize( void ) { (*this) /= norm(); } Vector Vector :: unit( void ) const { return (*this) / norm(); } Vector Vector :: abs( void ) const { return Vector( fabs( x ), fabs( y ), fabs( z ) ); } double dot( const Vector& u, const Vector& v ) { return u.x*v.x + u.y*v.y + u.z*v.z ; } Vector cross( const Vector& u, const Vector& v ) { return Vector( u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x ); } std::ostream& operator << (std::ostream& os, const Vector& o) { os << "[ " << o.x << " " << o.y << " " << o.z << " ]"; return os; } } ================================================ FILE: Flatten/src/Vertex.cpp ================================================ #include using namespace std; #include "Vertex.h" #include "Mesh.h" #include "HalfEdge.h" namespace DDG { double Vertex::area( void ) const // returns the dual area associated with this vertex { double A = 0.; HalfEdgeCIter h = he; do { if (not h->onBoundary) A += h->face->area(); h = h->flip->next; } while( h != he ); return A / 3.; } Vector Vertex::normal( void ) const // returns the vertex normal { Vector N; HalfEdgeCIter h = he; do { if (not h->onBoundary) N += h->face->normal(); h = h->flip->next; } while( h != he ); return N.unit(); } vector isolated; // all isolated vertices point to isolated.begin() bool Vertex::isIsolated( void ) const // returns true if the vertex is not contained in any face or edge; false otherwise { return he == isolated.begin(); } int Vertex :: valence( void ) const // returns the number of incident faces { int n = 0; HalfEdgeCIter h = he; do { n++; h = h->flip->next; } while( h != he ); return n; } void Vertex :: toggleTag() { tag = !tag; } } ================================================ FILE: Flatten/src/Viewer.cpp ================================================ #include #include #include #include #include #include #include using namespace std; #include "Viewer.h" #include "Image.h" #include "Application.h" namespace DDG { // declare static member variables Mesh Viewer::mesh; GLuint Viewer::surfaceDL = 0; int Viewer::windowSize[2] = { 512, 512 }; Camera Viewer::camera; Shader Viewer::shader; bool Viewer::renderWireframe = false; bool Viewer::render3D = false; bool Viewer::renderQuasiConformal = false; void Viewer :: init( void ) { restoreViewerState(); initGLUT(); setGL(); initGLSL(); updateDisplayList(); glutMainLoop(); } void Viewer :: initGLUT( void ) { int argc = 0; vector< vector > argv(1); // initialize window glutInitWindowSize( windowSize[0], windowSize[1] ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); glutInit( &argc, (char**)&argv ); glutCreateWindow( "DDG" ); // specify callbacks glutDisplayFunc ( Viewer::display ); glutIdleFunc ( Viewer::idle ); glutKeyboardFunc ( Viewer::keyboard ); glutSpecialFunc ( Viewer::special ); glutMouseFunc ( Viewer::mouse ); glutMotionFunc ( Viewer::motion ); // initialize menus int viewMenu = glutCreateMenu( Viewer::view ); glutSetMenu( viewMenu ); glutAddMenuEntry( "[f] Wireframe", menuWireframe ); glutAddMenuEntry( "[s] Switch 2D/3D", menuRender3D ); glutAddMenuEntry( "[q] Quasi Conformal Error", menuQuasiConformal ); glutAddMenuEntry( "[↑] Zoom In", menuZoomIn ); glutAddMenuEntry( "[↓] Zoom Out", menuZoomOut ); int mainMenu = glutCreateMenu( Viewer::menu ); glutSetMenu( mainMenu ); glutAddMenuEntry( "[space] Process Mesh", menuProcess ); glutAddMenuEntry( "[r] Reset Mesh", menuResetMesh ); glutAddMenuEntry( "[w] Write Mesh", menuWriteMesh ); glutAddMenuEntry( "[\\] Screenshot", menuScreenshot ); glutAddMenuEntry( "[esc] Exit", menuExit ); glutAddSubMenu( "View", viewMenu ); glutAttachMenu( GLUT_RIGHT_BUTTON ); } void Viewer :: initGLSL( void ) { shader.loadVertex( "shaders/vertex.glsl" ); shader.loadFragment( "shaders/fragment.glsl" ); } void Viewer :: menu( int value ) { switch( value ) { case( menuProcess ): mProcess(); break; case( menuResetMesh ): mResetMesh(); break; case( menuWriteMesh ): mWriteMesh(); break; case( menuScreenshot ): mScreenshot(); break; case( menuExit ): mExit(); break; default: break; } } void Viewer :: view( int value ) { switch( value ) { case( menuQuasiConformal ): mQuasiConformal(); break; case( menuRender3D ): mRender3D(); break; case( menuWireframe ): mWireframe(); break; case( menuZoomIn ): mZoomIn(); break; case( menuZoomOut ): mZoomOut(); break; default: break; } } void Viewer :: keyboard( unsigned char c, int x, int y ) { switch( c ) { case 'q': mQuasiConformal(); break; case 's': mRender3D(); break; case 'f': mWireframe(); break; case 'w': mWriteMesh(); break; case 'r': mResetMesh(); break; case '\\': mScreenshot(); break; case ' ': mProcess(); break; case 27: mExit(); break; default: break; } } void Viewer :: special( int i, int x, int y ) { switch( i ) { case GLUT_KEY_UP: camera.zoomIn(); break; case GLUT_KEY_DOWN: camera.zoomOut(); break; case 27: mExit(); break; default: break; } } void Viewer :: mouse( int button, int state, int x, int y ) { if( ( glutGetModifiers() & GLUT_ACTIVE_SHIFT) and state == GLUT_UP ) pickVertex(x, y); else camera.mouse( button, state, x, y ); } void Viewer :: motion( int x, int y ) { camera.motion( x, y ); } void Viewer :: idle( void ) { camera.idle(); glutPostRedisplay(); } void Viewer :: storeViewerState( void ) { ofstream out( ".viewer_state.txt" ); out << camera.rLast[0] << endl; out << camera.rLast[1] << endl; out << camera.rLast[2] << endl; out << camera.rLast[3] << endl; GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); out << view[2] << endl; out << view[3] << endl; } void Viewer :: restoreViewerState( void ) { ifstream in( ".viewer_state.txt" ); if( !in.is_open() ) return; in >> camera.rLast[0]; in >> camera.rLast[1]; in >> camera.rLast[2]; in >> camera.rLast[3]; in >> windowSize[0]; in >> windowSize[1]; } void Viewer :: mProcess( void ) { Application app; app.run(mesh); updateDisplayList(); } void Viewer :: mResetMesh( void ) { mesh.reload(); updateDisplayList(); } void Viewer :: mWriteMesh( void ) { mesh.write( "out.obj" ); } void Viewer :: mExit( void ) { //storeViewerState(); exit( 0 ); } void Viewer :: mWireframe( void ) { renderWireframe = !renderWireframe; updateDisplayList(); } void Viewer :: mRender3D( void ) { render3D = !render3D; updateDisplayList(); } void Viewer :: mQuasiConformal( void ) { renderQuasiConformal = !renderQuasiConformal; updateDisplayList(); } void Viewer :: mZoomIn( void ) { camera.zoomIn(); } void Viewer :: mZoomOut( void ) { camera.zoomOut(); } void Viewer :: mScreenshot( void ) { static int index = 0; // get window width and height GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); int w = view[2]; int h = view[3]; // get pixels Image image( w, h ); glReadPixels( 0, 0, w, h, GL_BGR, GL_FLOAT, &image(0,0) ); stringstream filename; filename << "frames/viewer" << setw(8) << setfill( '0' ) << index << ".tga"; image.write( filename.str().c_str() ); index++; } void Viewer :: display( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); shader.enable(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); double aspect = (double) viewport[2] / (double) viewport[3]; const double fovy = 50.; const double clipNear = .01; const double clipFar = 1000.; gluPerspective( fovy, aspect, clipNear, clipFar ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); Quaternion eye = Vector( 0., 0., -2.5*camera.zoom ); Quaternion center = Vector( 0., 0., 0. ); Quaternion up = Vector( 0., 1., 0. ); gluLookAt( eye[1], eye[2], eye[3], center[1], center[2], center[3], up[1], up[2], up[3] ); Quaternion r = camera.currentRotation(); eye = r.conj() * eye * r; GLint uniformEye = glGetUniformLocation( shader, "eye" ); glUniform3f( uniformEye, eye[1], eye[2], eye[3] ); Quaternion light = Vector( -1., 1., -2. ); light = r.conj() * light * r; GLint uniformLight = glGetUniformLocation( shader, "light" ); glUniform3f( uniformLight, light[1], light[2], light[3] ); camera.setView(); callDisplayList(); shader.disable(); glutSwapBuffers(); } void Viewer :: updateDisplayList( void ) { if( surfaceDL ) { glDeleteLists( surfaceDL, 1 ); surfaceDL = 0; } surfaceDL = glGenLists( 1 ); glNewList( surfaceDL, GL_COMPILE ); setMeshMaterial(); drawScene(); glEndList(); } void Viewer :: setGL( void ) { glClearColor( .5, .5, .5, 1. ); setLighting(); } void Viewer :: setLighting( void ) { GLfloat position[4] = { 20., 30., 40., 0. }; glLightfv( GL_LIGHT0, GL_POSITION, position ); glEnable( GL_LIGHT0 ); glEnable( GL_NORMALIZE ); } void Viewer :: setMeshMaterial( void ) { GLfloat diffuse[4] = { .8, .5, .3, 1. }; GLfloat specular[4] = { .3, .3, .3, 1. }; GLfloat ambient[4] = { .2, .2, .5, 1. }; glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular ); glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient ); glMaterialf ( GL_FRONT_AND_BACK, GL_SHININESS, 16. ); } void Viewer :: callDisplayList( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_DEPTH_TEST ); glEnable( GL_LIGHTING ); glCallList( surfaceDL ); glPopAttrib(); } void Viewer :: drawScene( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1., 1. ); drawPolygons(); glDisable( GL_POLYGON_OFFSET_FILL ); if( renderWireframe ) drawWireframe(); drawIsolatedVertices(); drawSelectedVertices(); glPopAttrib(); } void Viewer :: drawPolygons( void ) { if( not renderQuasiConformal ) { glColor3d( 1., .5, .25 ); } for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { if( f->isBoundary() ) continue; if( renderQuasiConformal ) { double value = faceQCDistortion( f ); Vector color = qcColor( value ); glColor3dv( &color[0] ); } glBegin( GL_POLYGON ); if( render3D and renderWireframe ) { Vector N = f->normal(); glNormal3dv( &N[0] ); } HalfEdgeCIter he = f->he; do { if( render3D and (not renderWireframe) ) { Vector N = he->vertex->normal(); glNormal3dv( &N[0] ); } if( not render3D ) { glNormal3d(0.,0.,1.); } if( render3D ) { glVertex3dv( &he->vertex->position[0] ); } else { glVertex3dv( &he->vertex->texture[0] ); } he = he->next; } while( he != f->he ); glEnd(); } } void Viewer :: drawWireframe( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glDisable( GL_LIGHTING ); glColor4f( 0., 0., 0., 0.5 ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBegin( GL_LINES ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { if( render3D ) { glVertex3dv( &e->he->vertex->position[0] ); glVertex3dv( &e->he->flip->vertex->position[0] ); } else { glVertex3dv( &e->he->vertex->texture[0] ); glVertex3dv( &e->he->flip->vertex->texture[0] ); } } glEnd(); glPopAttrib(); } void Viewer :: drawIsolatedVertices( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glPointSize( 5 ); glHint( GL_POINT_SMOOTH_HINT, GL_NICEST ); glEnable( GL_POINT_SMOOTH ); glColor3f( 1., 0., 0. ); glBegin( GL_POINTS ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { if( render3D ) { glVertex3dv( &v->position[0] ); } else { glVertex3dv( &v->texture[0] ); } } } glEnd(); glPopAttrib(); } void Viewer :: drawVertices( void ) { for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { glLoadName(v->index); glBegin(GL_POINTS); if( render3D ) { glVertex3dv( &v->position[0] ); } else { glVertex3dv( &v->texture[0] ); } glEnd(); } } void Viewer :: drawSelectedVertices( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable(GL_COLOR_MATERIAL); glHint( GL_POINT_SMOOTH_HINT, GL_NICEST ); glEnable( GL_POINT_SMOOTH ); glColor3f( 0., 0., 0.5 ); glPointSize( 20 ); glBegin(GL_POINTS); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->tag ) { if( render3D ) { glVertex3dv( &v->position[0] ); } else { glVertex3dv( &v->texture[0] ); } } } glEnd(); glPopAttrib(); } void Viewer :: pickVertex(int x, int y) { int width = glutGet(GLUT_WINDOW_WIDTH ); int height = glutGet(GLUT_WINDOW_HEIGHT); if( x < 0 || x >= width || y < 0 || y >= height ) return; int bufSize = mesh.vertices.size(); GLuint* buf = new GLuint[bufSize]; glSelectBuffer(bufSize, buf); GLint viewport[4]; GLdouble projection[16]; glGetIntegerv( GL_VIEWPORT, viewport ); glGetDoublev(GL_PROJECTION_MATRIX, projection); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix(x, viewport[3]-y, 10, 10, viewport); glMultMatrixd(projection); glMatrixMode(GL_MODELVIEW); glPushMatrix(); drawVertices(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); long hits = glRenderMode(GL_RENDER); int index = -1; double min_z = 1.0e100; for( long i = 0; i < hits; ++i ) { double distance = buf[4*i + 1]; if( distance < min_z ) { index = buf[4*i + 3]; min_z = distance; } } delete[] buf; if (index >= 0) { mesh.vertices[index].toggleTag(); updateDisplayList(); } } } ================================================ FILE: Flatten/src/main.cpp ================================================ #include using namespace std; #include "Viewer.h" #include "DenseMatrix.h" using namespace DDG; int main( int argc, char** argv ) { if( argc != 2 ) { cerr << "usage: " << argv[0] << " in.obj" << endl; return 1; } Viewer viewer; viewer.mesh.read( argv[1] ); viewer.init(); return 0; } ================================================ FILE: Flatten/src/qcError.cpp ================================================ #include #include #include #include #include #include #include using namespace std; #include "Viewer.h" namespace DDG { Vector HSV(double h, double s, double v) { double r = 0., g = 0., b = 0.; if (s == 0) { r = v; g = v; b = v; } else { h = (h == 1 ? 0 : h) * 6; int i = (int)floor(h); double f = h - i; double p = v * (1 - s); double q = v * (1 - (s * f)); double t = v * (1 - s * (1 - f)); switch (i) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; default: break; } } return Vector(r, g, b); } Vector qcColor( double qc ) // standard color map for quasi-conformal distortion { // clamp to range [1,1.5] qc = max( 1., min( 1.5, qc )); // compute color return HSV( (2.0-4.0*(qc-1.0))/3.0, .7, 0.65 ); } double quasiConformalDistortion( Vector p1, Vector p2, Vector p3, Vector q1, Vector q2, Vector q3 ) // computes the quasi-conformal distortion in a triangle with // initial vertices (p1,p2,p3) and current vertices (q1,q2,q3) { // compute edge vectors Vector u1 = p2 - p1; Vector u2 = p3 - p1; Vector v1 = q2 - q1; Vector v2 = q3 - q1; // compute orthonormal bases Vector e1 = u1 / u1.norm(); Vector e2 = ( u2 - dot(u2,e1)*e1 ); e2 = e2 / e2.norm(); Vector f1 = v1 / v1.norm(); Vector f2 = ( v2 - dot(v2,f1)*f1 ); f2 = f2 / f2.norm(); // project onto bases p1 = Vector( 0., 0., 0. ); p2 = Vector( dot(u1,e1), dot(u1,e2), 0. ); p3 = Vector( dot(u2,e1), dot(u2,e2), 0. ); q1 = Vector( 0., 0., 0. ); q2 = Vector( dot(v1,f1), dot(v1,f2), 0. ); q3 = Vector( dot(v2,f1), dot(v2,f2), 0. ); double A = cross(p2-p1,p3-p1).norm(); Vector Ss = (q1*(p2.y-p3.y)+q2*(p3.y-p1.y)+q3*(p1.y-p2.y))/(2.*A); Vector St = (q1*(p3.x-p2.x)+q2*(p1.x-p3.x)+q3*(p2.x-p1.x))/(2.*A); double a = dot(Ss,Ss); double b = dot(Ss,St); double c = dot(St,St); double Gamma = sqrt(.5*((a+c)+sqrt(sqr(a-c)+4.*b*b))); double gamma = sqrt(.5*((a+c)-sqrt(sqr(a-c)+4.*b*b))); if( Gamma < gamma ) { swap( Gamma, gamma ); } return Gamma/gamma; } double faceQCDistortion( FaceCIter f ) { Vector p1 = f->he->vertex->position; Vector p2 = f->he->next->vertex->position; Vector p3 = f->he->next->next->vertex->position; Vector q1 = f->he->vertex->texture; Vector q2 = f->he->next->vertex->texture; Vector q3 = f->he->next->next->vertex->texture; return quasiConformalDistortion( p1, p2, p3, q1, q2, q3 ); } } ================================================ FILE: Geodesics/.viewer_state.txt ================================================ 0.126663 -0.0243652 -0.989174 0.0699843 815 767 ================================================ FILE: Geodesics/Makefile ================================================ ########################################################################################## # Specify library locations here (add or remove "#" marks to comment/uncomment lines for your platform) # Mac OS X DDG_INCLUDE_PATH = DDG_LIBRARY_PATH = DDG_BLAS_LIBS = -framework Accelerate DDG_SUITESPARSE_LIBS = -lspqr -lumfpack -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -ltbb -lm -lsuitesparseconfig DDG_OPENGL_LIBS = -framework OpenGL -framework GLUT # # Linux # DDG_INCLUDE_PATH = # DDG_LIBRARY_PATH = # DDG_BLAS_LIBS = -llapack -lblas -lgfortran # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut -lGL -lGLU -lX11 # # Windows / Cygwin # DDG_INCLUDE_PATH = -I/usr/include/opengl -I/usr/include/suitesparse # DDG_LIBRARY_PATH = -L/usr/lib/w32api -L/usr/lib/suitesparse # DDG_BLAS_LIBS = -llapack -lblas # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut32 -lglu32 -lopengl32 ######################################################################################## TARGET = geodesics CC = g++ LD = g++ CFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_INCLUDE_PATH) -I./include -I./src LFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_LIBRARY_PATH) LIBS = $(DDG_OPENGL_LIBS) $(DDG_SUITESPARSE_LIBS) $(DDG_BLAS_LIBS) ######################################################################################## ## !! Do not edit below this line HEADERS := $(wildcard include/*.h) SOURCES := $(wildcard src/*.cpp) OBJECTS := $(addprefix obj/,$(notdir $(SOURCES:.cpp=.o))) all: $(TARGET) $(TARGET): $(OBJECTS) $(LD) $(OBJECTS) -o $(TARGET) $(CFLAGS) $(LFLAGS) $(LIBS) obj/%.o: src/%.cpp ${HEADERS} $(CC) -c $< -o $@ $(CFLAGS) clean: rm -f $(OBJECTS) rm -f $(TARGET) rm -f $(TARGET).exe ================================================ FILE: Geodesics/include/Application.h ================================================ /* * Geodesics in Heat: A New Approach to Computing Distance Based on Heat Flow * Keenan Crane, Clarisse Weischedel, Max Wardetzky * To appear at ACM Transactions on Graphics * * TODO: pre-factorize matrices */ #ifndef DDG_APPLICATION_H #define DDG_APPLICATION_H #include "Mesh.h" #include "Real.h" #include "Utility.h" #include "DenseMatrix.h" #include "SparseMatrix.h" #include "DiscreteExteriorCalculus.h" namespace DDG { class Application { public: double run(double dt, Mesh& mesh) { // initial condiiton DenseMatrix u0; int nb = builImpulseSignal(mesh, u0); if( nb == 0 ) return 1.0; // DEC SparseMatrix star0; HodgeStar0Form::build( mesh, star0 ); SparseMatrix star1; HodgeStar1Form::build( mesh, star1 ); SparseMatrix d0; ExteriorDerivative0Form::build( mesh, d0 ); // zero Neumann boundary condition SparseMatrix L = d0.transpose() * star1 * d0; // make L positive-definite L += Real(1.0e-8)*star0; // heat flow for short interval dt *= sqr(mesh.meanEdgeLength()); SparseMatrix A = star0 + Real(dt) * L; DenseMatrix u; solvePositiveDefinite(A, u, u0); // extract geodesic computeVectorField(u, mesh); DenseMatrix div; computeDivergence(mesh, div); DenseMatrix phi; solvePositiveDefinite(L, phi, div); setMinToZero(phi); assignDistance(phi, mesh); return phi.norm(); } protected: int builImpulseSignal(const Mesh& mesh, DenseMatrix& x) const { int nb = 0; x = DenseMatrix(mesh.vertices.size()); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { x(v->index) = 0.0; if( v->tag ) { x(v->index) = 1.0; nb++; } } return nb; } void computeVectorField(const DenseMatrix& u, Mesh& mesh) { for( FaceIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) { if( f->isBoundary() ) continue; HalfEdgeIter hij = f->he; HalfEdgeIter hjk = hij->next; HalfEdgeIter hki = hjk->next; VertexIter vi = hij->vertex; VertexIter vj = hjk->vertex; VertexIter vk = hki->vertex; double ui = u(vi->index); double uj = u(vj->index); double uk = u(vk->index); Vector eij90 = hij->rotatedEdge(); Vector ejk90 = hjk->rotatedEdge(); Vector eki90 = hki->rotatedEdge(); Vector X = 0.5 * ( ui*ejk90 + uj*eki90 + uk*eij90 ) / f->area(); f->vector = - X.unit(); } } void computeDivergence(const Mesh& mesh, DenseMatrix& div) const { div = DenseMatrix(mesh.vertices.size()); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++) { double sum = 0.0; HalfEdgeIter he = v->he; do { if( not he->onBoundary ) { Vector n = he->next->rotatedEdge(); Vector v = he->face->vector; sum += dot( n, v ); } he = he->flip->next; } while( he != v->he ); div(v->index) = sum; } } void assignDistance(const DenseMatrix& phi, Mesh& mesh) { for( VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++) { v->distance = phi(v->index); } } void setMinToZero(DenseMatrix& phi) const { double minValue = 1.0e100; for( int i = 0; i < phi.nRows(); ++i ) for( int j = 0; j < phi.nColumns(); ++j ) minValue = std::min( minValue, (double) phi(i,j) ); for( int i = 0; i < phi.nRows(); ++i ) for( int j = 0; j < phi.nColumns(); ++j ) phi(i,j) -= minValue; } }; } #endif ================================================ FILE: Geodesics/include/Camera.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Camera.h // ----------------------------------------------------------------------------- // // Camera is used by Viewer to keep track of the view state; it also // handles mouse input related to camera manipulation. // #ifndef DDG_CAMERA_H #define DDG_CAMERA_H #include "Quaternion.h" #ifdef __CYGWIN__ #define GLUT_DISABLE_ATEXIT_HACK #include #include #include #include #else #include #endif namespace DDG { class Camera { public: Camera( void ); // constructor Quaternion clickToSphere( int x, int y ); // projects a mous click onto the unit sphere void setView( void ) const; // applies the camera transformation to the OpenGL modelview stack void mouse( int button, int state, int x, int y ); // handles mouse clicks void motion( int x, int y ); // handles mouse drags void idle( void ); // handles camera momentum void zoomIn( void ); // moves viewer toward object void zoomOut( void ); // moves viewer away from object Quaternion currentRotation( void ) const; // returns the rotation corresponding to the current mouse state Quaternion pClick; // mouse coordinates of current click Quaternion pDrag; // mouse coordinates of current drag Quaternion pLast; // mouse coordinates of previous drag Quaternion rLast; // previous camera rotation Quaternion momentum; // camera momentum int tLast; // time of previous drag double zoom, vZoom; // zoom and zoom velocity }; } #endif ================================================ FILE: Geodesics/include/Complex.h ================================================ #ifndef DDG_COMPLEX_H #define DDG_COMPLEX_H #include namespace DDG { class Complex { public: Complex( double a=0., double b=0. ); // constructs number a+bi void operator+=( const Complex& z ); // add z void operator-=( const Complex& z ); // subtract z void operator*=( const Complex& z ); // Complex multiply by z void operator*=( double r ); // scalar multiply by r void operator/=( double r ); // scalar divide by r void operator/=( const Complex& z ); // complex divide by r Complex operator-( void ) const; // returns the additive inverse Complex conj( void ) const; // returns Complex conjugate Complex inv( void ) const; // returns inverse double arg( void ) const; // returns argument double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Complex unit( void ) const; // returns complex number with unit norm and same modulus Complex exponential( void ) const; // complex exponentiation double re; // real part double im; // imaginary part }; Complex operator+( const Complex& z1, const Complex& z2 ); // binary addition Complex operator-( const Complex& z1, const Complex& z2 ); // binary subtraction Complex operator*( const Complex& z1, const Complex& z2 ); // binary Complex multiplication Complex operator*( const Complex& z, double r ); // right scalar multiplication Complex operator*( double r, const Complex& z ); // left scalar multiplication Complex operator/( const Complex& z, double r ); // scalar division Complex operator/( const Complex& z1, const Complex& z2 ); // complex division double dot( const Complex& z1, const Complex& z2 ); // inner product double cross( const Complex& z1, const Complex& z2 ); // cross product std::ostream& operator<<( std::ostream& os, const Complex& o ); // prints components } #endif ================================================ FILE: Geodesics/include/DenseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DenseMatrix.h // ----------------------------------------------------------------------------- // // DenseMatrix represents an m by n (real or complex) matrix where every // entry -- including zero-valued entries -- is stored explicitly. This // class is most commonly used to represent dense vectors in sparse linear // systems (i.e., the right hand side and the solution vector). // // A real or complex matrix is allocated via // // DenseMatrix A( m, n ); // DenseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // DenseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a DenseMatrix returns a cholmod_dense* // which can be used by routines in SuiteSparse. For basic operations, however, // you should not need to access this pointer explicitly -- see the solve() // method in SparseMatrix.h. // #ifndef DDG_DENSEMATRIX_H #define DDG_DENSEMATRIX_H #include #include "Types.h" #include namespace DDG { enum NormType { lInfinity, lOne, lTwo }; template class DenseMatrix { public: DenseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix (specifying just m yields a column vector) DenseMatrix( const DenseMatrix& A ); // copy constructor const DenseMatrix& operator=( const DenseMatrix& B ); // copies B ~DenseMatrix( void ); // destructor SparseMatrix sparse( void ); // converts to a sparse matrix int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val = 0. ); // sets all elements to val double norm( NormType type = lInfinity ) const; // returns the maximum magnitude of any entry T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element of the matrix (uses 0-based indexing) T& operator()( int index ); T operator()( int index ) const; // access the specified element of a vector (uses 0-based indexing) DenseMatrix transpose( void ) const; // returns the transpose of this matrix DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c DenseMatrix operator+( const DenseMatrix& B ) const; // returns sum of this matrix with B void operator+=( const DenseMatrix& B ); // adds B to this matrix DenseMatrix operator-( const DenseMatrix& B ) const; // returns difference of this matrix with B void operator-=( const DenseMatrix& B ); // subtracts B from this matrix DenseMatrix operator-( void ) const; // returns additive inverse of this matrix cholmod_dense* to_cholmod( void ); // returns pointer to copy of matrix in CHOLMOD format const DenseMatrix& operator=( cholmod_dense* B ); // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B void normalize( void ); // divides by Frobenius norm T sum( void ) const; // returns the sum of all entries void removeMean( void ); // removes the mean void randomize( void ); // replaces entries with uniformly distributed random real numbers in the interval [-1,1] protected: int m, n; std::vector data; cholmod_dense* cData; }; template DenseMatrix operator*( const DenseMatrix& A, const T& c ); // right scalar multiplication template DenseMatrix operator*( const T& c, const DenseMatrix& A ); // left scalar multiplication template DenseMatrix operator/( const DenseMatrix& A, const T& c ); // scalar division template T dot( const DenseMatrix& x, const DenseMatrix& y ); // returns Euclidean inner product of x and y template std::ostream& operator << (std::ostream& os, const DenseMatrix& o); // prints entries template T inner( const DenseMatrix& x, const DenseMatrix& y ); // standard inner product template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ); // inner product with respect to a diagonal inner // product B represented as a dense vector } #include "DenseMatrix.inl" #endif ================================================ FILE: Geodesics/include/DiscreteExteriorCalculus.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DiscreteExteriorCalculus.h // ----------------------------------------------------------------------------- // // Static methods for building the fundamental discrete operators (exterior // derivative, Hodge star) for 0-, 1-, and 2-forms on a surface mesh. Methods // are templated on entry type, i.e., one can build either real- or complex- // matrices using the types DDG::Real and DDG::Complex, respectively. For // instance, to build the usual Laplacian on functions, one could write // // Mesh mesh; // SparseMatrix d0, star0, star1, Delta; // // ExteriorDerivative0Form::build( mesh, d0 ); // HodgeStar0Form::build( mesh, star0 ); // HodgeStar1Form::build( mesh, star1 ); // Delta = star0.inverse() * d0.transpose() * star1 * d0; // #ifndef DDG_DISCRETEEXTERIORCALCULUS_H #define DDG_DISCRETEEXTERIORCALCULUS_H #include "Mesh.h" #include "SparseMatrix.h" namespace DDG { template< class T > struct HodgeStar0Form { static void build( const Mesh& mesh, SparseMatrix& star0 ); }; template< class T > struct HodgeStar1Form { static void build( const Mesh& mesh, SparseMatrix& star1 ); }; template< class T > struct HodgeStar2Form { static void build( const Mesh& mesh, SparseMatrix& star2 ); }; template< class T > struct ExteriorDerivative0Form { static void build( const Mesh& mesh, SparseMatrix& d0 ); }; template< class T > struct ExteriorDerivative1Form { static void build( const Mesh& mesh, SparseMatrix& d1 ); }; } #include "DiscreteExteriorCalculus.inl" #endif ================================================ FILE: Geodesics/include/Edge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Edge.h // ----------------------------------------------------------------------------- // // Edge stores attributes associated with a mesh edge. The iterator he points // to one of its two associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_EDGE_H #define DDG_EDGE_H #include "Types.h" namespace DDG { class Edge { public: HalfEdgeIter he; // points to one of the two halfedges associated with this edge int index; // unique integer ID in the range 0, ..., nEdges-1 Edge() : index(0) { } }; } #endif ================================================ FILE: Geodesics/include/Face.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Face.h // ----------------------------------------------------------------------------- // // Face stores attributes associated with a mesh edge. The iterator he points // to one of its associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_FACE_H #define DDG_FACE_H #include "Types.h" #include "Vector.h" namespace DDG { class Face { public: HalfEdgeIter he; // points to one of the halfedges associated with this face int index; // unique integer ID in the range 0, ..., nFaces-1 Vector vector; // tangent vector per triangle Face() : index(0) { } bool isBoundary( void ) const; // returns true if this face corresponds to a // boundary loop; false otherwise double area( void ) const; // returns the triangle area Vector normal( void ) const; // returns the unit normal associated with this face; normal // orientation is determined by the circulation order of halfedges Vector circumcenter( void ) const; // returns triangle circumcenter Vector barycenter( void ) const; // returns triangle barycenter }; } #endif ================================================ FILE: Geodesics/include/HalfEdge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- HalfEdge.h // ----------------------------------------------------------------------------- // // HalfEdge is used to define mesh connectivity. (See the documentation for a // more in-depth discussion of the halfedge data structure.) // #ifndef DDG_HALFEDGE_H #define DDG_HALFEDGE_H #include "Vector.h" #include "Types.h" namespace DDG { class HalfEdge { public: HalfEdgeIter next; // points to the next halfedge around the current face HalfEdgeIter flip; // points to the other halfedge associated with this edge VertexIter vertex; // points to the vertex at the "tail" of this halfedge EdgeIter edge; // points to the edge associated with this halfedge FaceIter face; // points to the face containing this halfedge bool onBoundary; // true if this halfedge is contained in a boundary // loop; false otherwise Vector texcoord; // texture coordinates associated with the triangle corner at the // "tail" of this halfedge double cotan( void ) const; // returns the cotangent of the angle opposing this edge Vector rotatedEdge( void ) const; // returns oriented edge vector rotated by PI/2 around face normal // if onBoundary, then return nil }; } #endif ================================================ FILE: Geodesics/include/Image.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Image.h // ----------------------------------------------------------------------------- // // Image represents a color bitmap image. A simple example might look like // // Image im; // im.read( "input.tga" ); // // modify image data via im(x,y) = ...; // im.write( "output.tga" ); // #ifndef DDG_IMAGE_H #define DDG_IMAGE_H #include #include namespace DDG { class Image { public: Image( int width = 0, int height = 0 ); // constructs image with specified width and height float& operator()( int x, int y ); const float& operator()( int x, int y ) const; // accesses pixel (x,y) float sample( float x, float y ) const; // samples image at (x,y) using bilinear filtering int width( void ) const; int height( void ) const; // returns image dimensions void read( const char* filename ); // loads an image file in Truevision TGA format // (must be RGB image with 24 or 32 bits per pixel) void write( const char* filename ) const; // writes an image file in Truevision TGA format // (RGB image with 24 bits per pixel) protected: void clamp( int& x, int& y ) const; // clamps coordinates to range [0,w-1] x [0,h-1] int w, h; // width and height std::vector pixels; // interleaved RGBA pixel data in range [0-1] }; } #endif ================================================ FILE: Geodesics/include/LinearContext.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearContext.h // ----------------------------------------------------------------------------- // // LinearContext is the global solver context needed to interface with the // SuiteSparse library. It is essentially a wrapper around cholmod_common. A // single static instance of LinearContext is declared in LinearContext.cpp and // is shared by all instances of DenseMatrix, SparseMatrix, and LinearSystem. // In other words, you shouldn't have to instantiate LinearContext yourself // unless you're doing something really fancy! // #ifndef DDG_LINEARSOLVERCONTEXT #define DDG_LINEARSOLVERCONTEXT #include namespace DDG { class LinearContext { public: LinearContext( void ); // constructor ~LinearContext( void ); // destructor operator cholmod_common*( void ); // allows LinearContext to be treated as a cholmod_common* protected: cholmod_common context; }; } #endif ================================================ FILE: Geodesics/include/LinearEquation.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearEquation.h // ----------------------------------------------------------------------------- // // LinearEquation represents an equation with an arbitrary linear polynomial on // both the left- and right-hand side. It is primarily used while building a // LinearSystem. For convenience, operator== is overloaded so that the user // can construct a LinearEquation by writing something that looks much like the // usual mathematical syntax for a linear equation. For example, // // LinearEquation eqn = ( x + 2*y == 3*z ); // // builds the linear equation x + 2y = 3z. // #ifndef DDG_LINEAREQUATION_H #define DDG_LINEAREQUATION_H #include "LinearPolynomial.h" namespace DDG { class LinearEquation { public: LinearPolynomial lhs; // left-hand side LinearPolynomial rhs; // right-hand side }; LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ); // constructs a linear equation with the specified left- and right-hand side } #endif ================================================ FILE: Geodesics/include/LinearPolynomial.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearPolynomial.h // ----------------------------------------------------------------------------- // // LinearPolynomial represents an affine function of the form // // f(x1,x2,...,xn) = c1 x1 + c2 x2 + ... + cn xn + d // // where the xi are real-valued variables with real coefficients ci, and d is // a real constant. The variables and their coefficients are represented using // instances of the Variable class. LinearPolynomial implements all the usual // algebraic operations on affine functions, as well as type conversions from // more elementary types (scalars, single variables, etc.). // // Importantly, variables used in a LinearPolynomial should *not* be deallocated // while the polynomial is still in use -- LinearPolynomial stores only a // reference to these variables so that the solution to a linear system can be // automatically copied back into the variables. // #ifndef DDG_LINEARPOLYNOMIAL_H #define DDG_LINEARPOLYNOMIAL_H #include #include #include "Variable.h" namespace DDG { class LinearPolynomial { public: LinearPolynomial( void ); // constructs the zero function LinearPolynomial( double c ); // constructs the constant function with value c LinearPolynomial( Variable& v ); // constructs a function with a single variable v const LinearPolynomial& operator=( double c ); // assigns the constant function with value c const LinearPolynomial& operator=( Variable& v ); // assigns a function with a single variable v void operator+=( double c ); void operator-=( double c ); void operator*=( double c ); void operator/=( double c ); // adds, subtract, multiplies, or divides by a constant void operator+=( Variable& v ); void operator-=( Variable& v ); // increments or decrements by a single variable v void operator+=( const LinearPolynomial& p ); void operator-=( const LinearPolynomial& p ); // increments or decrements by an affine function LinearPolynomial operator-( void ) const; // returns the additive inverse (i.e., negation) double evaluate( void ) const; // evaluates the function using the current values of its variables std::map linearTerms; // list of linear terms double constantTerm; // constant term }; LinearPolynomial operator+( double c, Variable& v ); // sum LinearPolynomial operator+( Variable& v, double c ); // sum LinearPolynomial operator-( double c, Variable& v ); // difference LinearPolynomial operator-( Variable& v, double c ); // difference LinearPolynomial operator*( double c, Variable& v ); // product LinearPolynomial operator*( Variable& v, double c ); // product LinearPolynomial operator/( Variable& v, double c ); // quotient // algebraic operations between single variables and constants LinearPolynomial operator+( Variable& v1, Variable& v2 ); // sum LinearPolynomial operator-( Variable& v1, Variable& v2 ); // difference // algebraic operations between pairs of variables LinearPolynomial operator+( const LinearPolynomial& p, double c ); // sum LinearPolynomial operator+( double c, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, double c ); // difference LinearPolynomial operator-( double c, const LinearPolynomial& p ); // difference LinearPolynomial operator*( const LinearPolynomial& p, double c ); // product LinearPolynomial operator*( double c, const LinearPolynomial& p ); // product LinearPolynomial operator/( const LinearPolynomial& p, double c ); // quotient // algebraic operations between polynomials and constants LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ); // sum LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ); // difference LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ); // difference // algebraic operations between polynomials and single variables LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ); // sum LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ); // difference // algebraic operations between pairs of polynomials std::ostream& operator<<( std::ostream& os, const LinearPolynomial& p ); // prints the symbolic representation of a polynomial (all variables must be named) } #endif ================================================ FILE: Geodesics/include/LinearSystem.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearSystem.h // ----------------------------------------------------------------------------- // // LinearSystem represents a system of linear equations expressed in terms of // instances of the Variable class. The main idea is to make it easy to // construct and solve linear systems without explicitly think about variable // indices, matrix layout, etc. (This kind of abstraction is particularly // useful for debugging and rapid prototyping.) See the documentation for // examples of building linear and solving systems. // // Importantly, any variable used by a LinearSystem should not be deallocated // while the system is still in use, because the method LinearSystem::solve() // automatically copies the solution back into the variables used to define the // equations. (In the future variables may become reference-counted in order // to avoid this issue.) // // Note that LinearSystem::solve() uses a general-purpose linear solver (namely // the sparse QR factorization found in SuiteSparse) that is quite fast but may // not always be your best option. To improve performance you may want to // build the system explicitly using an instance of SparseMatrix and call a // more specialized solver. (In the future there may be options for specifying // that a LinearSystem is, e.g., symmetric and positive-definite.) // #ifndef DDG_LINEARSYSTEM_H #define DDG_LINEARSYSTEM_H #include #include "LinearEquation.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "Real.h" namespace DDG { class LinearSystem { public: void clear( void ); // removes all equations from the system void push_back( const LinearEquation& e ); // appends the equation e to the sytem void solve( void ); // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution std::vector equations; // the collection of equations defining the system protected: void convertEquations( void ); void indexVariables( void ); void buildSparseMatrix( void ); void buildRightHandSide( void ); void computeSolution( void ); int nEquations; int nVariables; std::vector currentEquations; std::map index; SparseMatrix A; DenseMatrix x; DenseMatrix b; }; } #endif ================================================ FILE: Geodesics/include/Mesh.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Mesh.h // ----------------------------------------------------------------------------- // // Mesh represents a polygonal surface mesh using the halfedge data structure. // It is essentially a large collection of disjoint vertices, edges, and faces // that are ``glued together'' by halfedges which encode connectivity (see // the documentation for an illustration). By construction, the halfedge data // structure cannot represent nonorientable surfaces or meshes with nonmanifold // edges. // // Mesh elements are referenced using iterators -- common usage of these // iterators is to either traverse an entire vector of mesh elements: // // // visit all vertices // for( VertexIter i = vertices.begin(); i != vertices.end(); i++ ) // { // //... // } // // or to perform a local traversal over the neighborhood of some mesh element: // // // visit both halfedges of edge e // HalfEdgeIter he = e->he; // do // { // // ... // // he = he->flip; // } // while( he != e->he ); // // (See Types.h for an explicit definition of iterator types.) // // Meshes with boundary are handled by creating an additional face for each // boundary loop (the method Face::isBoundary() determines whether a given // face is a boundary loop). Isolated vertices (i.e., vertiecs not contained // in any edge or face) reference a dummy halfedge and can be checked via // the method Vertex::isIsolated(). // #ifndef DDG_MESH_H #define DDG_MESH_H #include #include #include "HalfEdge.h" #include "Vertex.h" #include "Edge.h" #include "Face.h" #include "SparseMatrix.h" namespace DDG { class Mesh { public: Mesh( void ); // constructs an empty mesh Mesh( const Mesh& mesh ); // constructs a copy of mesh const Mesh& operator=( const Mesh& mesh ); // copies mesh int read( const std::string& filename ); // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error int write( const std::string& filename ) const; // writes a mesh to a Wavefront OBJ file; return value is nonzero // only if there was an error bool reload( void ); // reloads a mesh from disk using the most recent input filename void normalize( void ); // centers around the origin and rescales to have unit radius double area( void ) const; // returns total mesh area double meanEdgeLength( void ) const; // returns mean edge lenght std::vector halfedges; std::vector vertices; std::vector edges; std::vector faces; std::vector boundaries; // storage for mesh elements protected: std::string inputFilename; void indexElements( void ); // assigns a unique, 0-based index to each mesh element }; } #endif ================================================ FILE: Geodesics/include/MeshIO.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- MeshIO.h // ----------------------------------------------------------------------------- // // MeshIO handles input/output operations for Mesh objects. Currently the only // supported mesh format is Wavefront OBJ -- for a format specification see // // http://en.wikipedia.org/wiki/Wavefront_.obj_file // // Note that vertex normals and material properties are currently ignored. // #ifndef DDG_MESHIO_H #define DDG_MESHIO_H #include #include #include #include namespace DDG { class Mesh; class Index; class MeshData; class MeshIO { public: static int read( std::istream& in, Mesh& mesh ); // reads a mesh from a valid, open input stream in static void write( std::ostream& out, const Mesh& mesh ); // writes a mesh to a valid, open output stream out protected: static int readMeshData( std::istream& in, MeshData& data ); static void readPosition( std::stringstream& ss, MeshData& data ); static void readTexCoord( std::stringstream& ss, MeshData& data ); static void readNormal ( std::stringstream& ss, MeshData& data ); static void readFace ( std::stringstream& ss, MeshData& data ); static Index parseFaceIndex( const std::string& token ); static void preallocateMeshElements( const MeshData& data, Mesh& mesh ); static int buildMesh( const MeshData& data, Mesh& mesh ); static void checkIsolatedVertices( const Mesh& Mesh ); static void checkNonManifoldVertices( const Mesh& Mesh ); }; } #endif ================================================ FILE: Geodesics/include/Quaternion.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Quaternion.h // ----------------------------------------------------------------------------- // // Quaternion represents an element of the quaternions, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // Hamilton product is expressed using the * operator: // // Quaternion p, q, r; // r = q * p; // // and conjugation is expressed using the method Quaternion::conj(): // // Quaternion q; // double normQSquared = -q.conj()*q; // // Individual components can be accessed in several ways: the real and imaginary // parts can be accessed using the methods Quaternion::re() and Quaternion::im(): // // Quaternion q; // double a = q.re(); // Vector b = q.im(); // // or by index: // // Quaternion q; // double a = q[0]; // double bi = q[1]; // double bj = q[2]; // double bk = q[3]; // #ifndef DDG_QUATERNION_H #define DDG_QUATERNION_H #include "Vector.h" #include "Complex.h" #include namespace DDG { class Quaternion { public: Quaternion( void ); // initializes all components to zero Quaternion( const Quaternion& q ); // initializes from existing quaternion Quaternion( double s, double vi, double vj, double vk ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s, const Vector& v ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s ); // initializes purely real quaternion with specified real (s) component (imaginary part is zero) Quaternion( const Vector& v ); // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) Quaternion( const Complex& z ); // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k const Quaternion& operator=( double s ); // assigns a purely real quaternion with real value s const Quaternion& operator=( const Vector& v ); // assigns a purely real quaternion with imaginary value v double& operator[]( int index ); // returns reference to the specified component (0-based indexing: r, i, j, k) const double& operator[]( int index ) const; // returns const reference to the specified component (0-based indexing: r, i, j, k) void toMatrix( double Q[4][4] ) const; // builds 4x4 matrix Q representing (left) quaternion multiplication double& re( void ); // returns reference to double part const double& re( void ) const; // returns const reference to double part Vector& im( void ); // returns reference to imaginary part const Vector& im( void ) const; // returns const reference to imaginary part Quaternion operator+( const Quaternion& q ) const; // addition Quaternion operator-( const Quaternion& q ) const; // subtraction Quaternion operator-( void ) const; // negation Quaternion operator*( double c ) const; // right scalar multiplication Quaternion operator/( double c ) const; // scalar division void operator+=( const Quaternion& q ); // addition / assignment void operator+=( double c ); // addition / assignment of pure real void operator-=( const Quaternion& q ); // subtraction / assignment void operator-=( double c ); // subtraction / assignment of pure real void operator*=( double c ); // scalar multiplication / assignment void operator/=( double c ); // scalar division / assignment Quaternion operator*( const Quaternion& q ) const; // Hamilton product void operator*=( const Quaternion& q ); // Hamilton product / assignment Quaternion conj( void ) const; // conjugation Quaternion inv( void ) const; // inverse double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Quaternion unit( void ) const; // returns unit quaternion void normalize( void ); // divides by Euclidean length protected: double s; // scalar (double) part Vector v; // vector (imaginary) part }; Quaternion operator*( double c, const Quaternion& q ); // left scalar multiplication std::ostream& operator<<( std::ostream& os, const Quaternion& q ); // prints components } #endif ================================================ FILE: Geodesics/include/Real.h ================================================ #ifndef DDG_REAL_H #define DDG_REAL_H namespace DDG { class Real { public: Real( double x = 0. ); // constructs real number with value x operator double( void ) const; // type cast to double void operator+=( double x ); // increment void operator-=( double x ); // decrement void operator*=( double x ); // multiply void operator/=( double x ); // divide Real conj( void ) const; // simply returns the value (for compatibility w/ complex numbers) Real inv( void ) const; // returns inverse double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Real unit( void ) const; // returns number with unit norm and same sign protected: double value; // value }; } #endif ================================================ FILE: Geodesics/include/Shader.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Shader.h // ----------------------------------------------------------------------------- // // Shader encapsulates the functionality of a shader program written in // the OpenGL Shader Language (GLSL). Basic usage is to read a collection // of source files to disk and enable the shader before making draw calls. // For instance, during initialization one might write // // Shader shader; // shader.loadVertex( "vertex.glsl" ); // shader.loadFragment( "fragment.glsl" ); // // and in the main draw routine write // // shader.enable(); // // draw some stuff // shader.disable(); // #ifndef DDG_SHADER_H #define DDG_SHADER_H #include #include namespace DDG { class Shader { public: Shader( void ); // constructor -- shader is initially invalid ~Shader( void ); // destructor void loadVertex( const char* filename ); // read vertex shader from GLSL source file void loadFragment( const char* filename ); // read fragment shader from GLSL source file void loadGeometry( const char* filename ); // read geometry shader from GLSL source file void enable( void ); // uses this shader for rendering void disable( void ) const; // uses the fixed-function pipeline for rendering operator GLuint( void ) const; // returns the ID of this shader program (for calls to OpenGL) protected: void load( GLenum shaderType, const char* filename, GLuint& shader ); bool readSource( const char* filename, std::string& source ); GLuint vertexShader; GLuint fragmentShader; GLuint geometryShader; GLuint program; bool linked; }; } #endif ================================================ FILE: Geodesics/include/SparseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- SparseMatrix.h // ----------------------------------------------------------------------------- // // SparseMatrix represents an m by n (real or complex) matrix where only // nonzero entries are stored explicitly. This class is most commonly // used to represent the linear term in sparse linear systems (i.e., the matrix // part). // // A real or complex matrix is allocated via // // SparseMatrix A( m, n ); // SparseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // SparseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a SparseMatrix returns a // cholmod_sparse* which can be used by routines in SuiteSparse. For basic // operations, however, you should not need to access this pointer explicitly -- // see the solve() method below. // // Internally SparseMatrix stores nonzero entries in a heap data structure; the // amortized cost of insertion is therefore no worse than the sorting cost of // putting the matrix in compressed-column order. // #ifndef DDG_SPARSE_MATRIX_H #define DDG_SPARSE_MATRIX_H #include #include #include #include "Types.h" namespace DDG { template class SparseMatrix { public: SparseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix SparseMatrix( const SparseMatrix& B ); // copy constructor ~SparseMatrix( void ); // destructor const SparseMatrix& operator=( const SparseMatrix& B ); // copies B const SparseMatrix& operator=( cholmod_sparse* B ); // copies a cholmod_sparse* into a SparseMatrix; // takes responsibility for deallocating B void resize( int m, int n ); // clears and resizes to mxn matrix SparseMatrix transpose( void ) const; // returns the transpose of this matrix cholmod_sparse* to_cholmod( void ); // returns pointer to copy of matrix in compressed-column CHOLMOD format SparseMatrix operator*( const SparseMatrix& B ) const; // returns product of this matrix with sparse B DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with dense B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c void operator+=( const SparseMatrix& B ); // adds B to this matrix void operator-=( const SparseMatrix& B ); // subtracts B from this matrix SparseMatrix operator+( const SparseMatrix& B ) const; // returns sum of this matrix with B SparseMatrix operator-( const SparseMatrix& B ) const; // returns difference of this matrix with B int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val ); // sets all nonzero elements val SparseMatrix inverse( void ) const; // returns inverse -- for diagonal matrices only // (assertion occurs for non-diagonal matrices) static SparseMatrix identity( int N ); // returns the N x N identity matrix DenseMatrix full( void ) const; // converts to a dense matrix T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element (uses 0-based indexing) // TODO for legibility, replace w/ type where entries are named "row, // TODO col" instead of "first, second" (especially since we adopt the // TODO unorthodox convention of storing the column first) typedef std::pair EntryIndex; // convenience type for an entry index; note that we store column THEN // row, which makes it easier to build compressed column format typedef std::map EntryMap; typedef typename EntryMap::iterator iterator; typedef typename EntryMap::const_iterator const_iterator; // convenience types for storing and accessing entries iterator begin( void ); const_iterator begin( void ) const; iterator end( void ); const_iterator end( void ) const; // return iterators to first and last nonzero entries void shift( double c ); // adds c times the identity matrix to this matrix protected: int m, n; EntryMap data; cholmod_sparse* cData; void allocateSparse( void ); void setEntry( const_iterator e, int i, double* pr ); }; template SparseMatrix operator*( const SparseMatrix& A, const T& c ); // right scalar multiplication template SparseMatrix operator*( const T& c, const SparseMatrix& A ); // left scalar multiplication template SparseMatrix operator/( const SparseMatrix& A, const T& c ); // scalar division template std::ostream& operator << (std::ostream& os, const SparseMatrix& o); // prints entries template class SparseFactor { public: SparseFactor( void ); ~SparseFactor( void ); void build( SparseMatrix& A ); // factorizes positive-definite matrix A using CHOLMOD bool valid( void ) const; // returns true if the factor has been built; false otherwise cholmod_factor* to_cholmod( void ); // returns pointer to underlying cholmod_factor data structure protected: cholmod_factor *L; }; template void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse QR factorization template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse LU factorization template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ); // backsolves the prefactored positive definite sparse linear system LL'x = b template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ); // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ); // returns the max residual of the linear problem A x = b relative to the largest entry of the solution template double residual( const SparseMatrix& A, const DenseMatrix& x ); // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda B x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns /<(B-EE^T)x,x> } #include "SparseMatrix.inl" #endif ================================================ FILE: Geodesics/include/Types.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Types.h // ----------------------------------------------------------------------------- // // This file contains forward declarations of common types and definitions of // convenience types for standard iterators. // #ifndef DDG_TYPES_H #define DDG_TYPES_H #include #include #include namespace DDG { // forward declarations class Camera; class Complex; class Edge; class Face; class HalfEdge; class Image; class LinearContext; class LinearEquation; class LinearPolynomial; class LinearSystem; class Mesh; class MeshIO; class Quaternion; class Real; class Shader; class Variable; class Vector; class Vertex; class Viewer; template class DenseMatrix; template class SparseMatrix; // convenience types for iterators typedef std::map::iterator TermIter; typedef std::map::const_iterator TermCIter; typedef std::vector::iterator PolyIter; typedef std::vector::const_iterator PolyCIter; typedef std::vector::iterator EqnIter; typedef std::vector::const_iterator EqnCIter; typedef std::map::iterator IndexIter; typedef std::map::const_iterator IndexCIter; typedef std::vector::iterator HalfEdgeIter; typedef std::vector::const_iterator HalfEdgeCIter; typedef std::vector::iterator VertexIter; typedef std::vector::const_iterator VertexCIter; typedef std::vector::iterator EdgeIter; typedef std::vector::const_iterator EdgeCIter; typedef std::vector::iterator FaceIter; typedef std::vector::const_iterator FaceCIter; } #endif ================================================ FILE: Geodesics/include/Utility.h ================================================ #ifndef DDG_UTILITY_H #define DDG_UTILITY_H #include #include "Utility.h" #include "Complex.h" namespace DDG { inline double sqr( double x ) { return x*x; } inline double unitRand( void ) { const double rRandMax = 1. / (double) RAND_MAX; return rRandMax * (double) rand(); } inline double seconds( int t0, int t1 ) { return (double)(t1-t0) / (double) CLOCKS_PER_SEC; } } namespace DDGConstants { static DDG::Complex ii( 0., 1. ); } #endif ================================================ FILE: Geodesics/include/Variable.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Variable.h // ----------------------------------------------------------------------------- // // Variable represents a variable that can be used to define a (linear or // nonlinear) system of equations. Its main feature is that it can be used // both as an abstract variable (e.g., when used to define an equation) but can // also store a definite numerical value. For instance, suppose we define a // linear polynomial // // Variable x, y; // LinearPolynomial p = x + 2*y; // // Now by assigning different numerical values to x and y, we can evaluate the // polynomial at different points: // // *x = 1; // *y = 2; // cout << p.evaluate() << endl; // // *x = 3; // *y = 4; // cout << p.evaluate() << endl; // // In general the dereference operator (*) accesses the numerical value. // Variables can also be named in order to aid with debugging. For instance, // // Variable x( "x" ); // Variable y( "y" ); // Polynomial p = x + 2*y; // cout << p << endl; // // will print out something like "x+2*y". // // The "fixed" flag in a variable refers to whether it is held constant while // solving a system of equations -- see the documentation for further discussion. // #ifndef DDG_VARIABLE_H #define DDG_VARIABLE_H #include namespace DDG { class Variable { public: Variable( double value = 0., bool fixed = false ); // initialize a variable which has value zero and is not fixed by default Variable( std::string name, double value = 0., bool fixed = false ); // initialize a named variable which has value zero and is not fixed by default double& operator*( void ); // returns a reference to the numerical value const double& operator*( void ) const; // returns a const reference to the numerical value std::string name; // names the variable (for display output) double value; // numerical value bool fixed; // true if a variable is held constant while solving a system of equations }; } #endif ================================================ FILE: Geodesics/include/Vector.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vector.h // ----------------------------------------------------------------------------- // // Vector represents an element of Euclidean 3-space, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // inner product (i.e., scalar or dot product) is expressed using the global // method dot(): // // Vector u, v; // double cosTheta = dot( u, v ); // // and the cross product is expressed using the global method cross(): // // Vector u, v, w; // w = cross( u, v ); // // Individual components can be accessed in two ways: either directly via the // members x, y, and z: // // Vector v; // cout << v.x << endl; // cout << v.y << endl; // cout << v.z << endl; // // or by index: // // Vector v; // for( int i = 0; i < 3; i++ ) // { // cout << v[i] << endl; // } // #ifndef DDG_VECTOR_H #define DDG_VECTOR_H #include namespace DDG { class Vector { public: Vector(); // initializes all components to zero Vector( double x, double y, double z); // initializes with specified components Vector( const Vector& v ); // initializes from existing vector double& operator[] ( const int& index ); // returns reference to the specified component (0-based indexing: x, y, z) const double& operator[] ( const int& index ) const; // returns const reference to the specified component (0-based indexing: x, y, z) Vector operator+( const Vector& v ) const; // addition Vector operator-( const Vector& v ) const; // subtraction Vector operator-( void ) const; // negation Vector operator*( const double& c ) const; // right scalar multiplication Vector operator/( const double& c ) const; // scalar division void operator+=( const Vector& v ); // addition / assignment void operator-=( const Vector& v ); // subtraction / assignment void operator*=( const double& c ); // scalar multiplication / assignment void operator/=( const double& c ); // scalar division / assignment double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Vector unit( void ) const; // returns unit vector void normalize( void ); // divides by Euclidean length Vector abs( void ) const; // returns vector containing magnitude of each component double x, y, z; // components }; Vector operator* ( const double& c, const Vector& v ); // left scalar multiplication double dot( const Vector& u, const Vector& v ); // dot product (a.k.a. inner or scalar product) Vector cross( const Vector& u, const Vector& v ); // cross product std::ostream& operator << (std::ostream& os, const Vector& o); // prints components } #endif ================================================ FILE: Geodesics/include/Vertex.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vertex.h // ----------------------------------------------------------------------------- // // Vertex stores attributes associated with a mesh edge. The iterator he // points to its "outgoing" halfedge. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_VERTEX_H #define DDG_VERTEX_H #include "Vector.h" #include "Types.h" namespace DDG { class Vertex { public: HalfEdgeIter he; // points to the "outgoing" halfedge Vector position; // location of vertex in Euclidean 3-space int index; // unique integer ID in the range 0, ..., nVertices-1 bool tag; // true if vertex is selected by the user; false otherwise double distance; Vertex() : index(0), tag(false), distance(0.0) { } double area( void ) const; // returns the barycentric area associated with this vertex Vector normal( void ) const; // returns the vertex normal bool isIsolated( void ) const; // returns true if the vertex is not contained in any face or edge; false otherwise int valence( void ) const; // returns the number of incident faces / edges void toggleTag(); // toggle vertex tag }; } #endif ================================================ FILE: Geodesics/include/Viewer.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Viewer.h // ----------------------------------------------------------------------------- // // Viewer provides a graphical user interface (GUI) for inspecting and // interacting with a Mesh object. Viewer methods are static in order // to make them compatible with GLUT callbacks. // #ifndef DDG_VIEWER_H #define DDG_VIEWER_H #include #include "Mesh.h" #include "Camera.h" #include "Shader.h" namespace DDG { class Viewer { public: static void init( void ); // displays the viewer until the program ends static Mesh mesh; // surface mesh visualized by Viewer static double maxDistance; // max geodesic distance protected: // init static void initGLUT( void ); static void initGLSL( void ); // GLUT callbacks static void display( void ); static void idle( void ); static void keyboard( unsigned char c, int x, int y ); static void special( int i, int x, int y ); static void mouse( int button, int state, int x, int y ); static void motion( int x, int y ); static void menu( int value ); static void view( int value ); // menu functions static void mProcess( void ); static void mResetMesh( void ); static void mWriteMesh( void ); static void mExit( void ); static void mWireframe( void ); static void mZoomIn( void ); static void mZoomOut( void ); static void mScreenshot( void ); static void mIncreaseStep( void ); static void mDecreaseStep( void ); static void mVectorField( void ); // unique identifiers for menus enum { menuProcess, menuResetMesh, menuWriteMesh, menuExit, menuWireframe, menuZoomIn, menuZoomOut, menuScreenshot, menuIncreaseStep, menuDecreaseStep, menuVectorField }; // draw routines static void setGL( void ); static void setLighting( void ); static void setMeshMaterial( void ); static void callDisplayList( void ); static void updateDisplayList( void ); static void drawScene( void ); static void drawPolygons( void ); static void drawWireframe( void ); static void drawVectorField( void ); static void drawVertices( void ); static void drawSelectedVertices( void ); static void drawIsolatedVertices( void ); static void pickVertex(int x, int y); static void storeViewerState( void ); static void restoreViewerState( void ); static int windowSize[2]; static bool renderWireframe; // draw wireframe static bool renderVectorField; static Camera camera; // keeps track of view state static GLuint surfaceDL; // display list for mesh static Shader shader; // shader used to determine appearance of surface static double step; static double delta; // fairing time step }; } #endif ================================================ FILE: Geodesics/obj/.empty ================================================ ================================================ FILE: Geodesics/shaders/fragment.glsl ================================================ uniform vec3 eye; uniform vec3 light; varying vec3 position; varying vec3 normal; float diffuse( vec3 N, vec3 L ) { return max( 0., dot( N, L )); } float specular( vec3 N, vec3 L, vec3 E ) { const float shininess = 8.; vec3 R = 2.*dot(L,N)*N - L; return pow( max( 0., dot( R, E )), shininess ); } float fresnel( vec3 N, vec3 E ) { const float sharpness = 10.; float NE = max( 0., dot( N, E )); return pow( sqrt( 1. - NE*NE ), sharpness ); } void main() { // color float d = 1. - gl_Color.r; float r = (1. - d*d) * .8; float g = (1. - (2. * (d - .5)) * (2. * (d - .5))) * .7; float b = (1. - (1. - d) * (1. - d)); vec3 color = vec3(r, g, b); // lines float h = gl_Color.r; h = h * 30.; h = h - floor( h ); h = (1. / (1. + exp(-100.*(h - .55)))) + (1. / (1. + exp(-100.*(-h + .45)))); h = 1. - h; color.xyz = vec3(h, h, h) + (1. - h) * color.xyz; vec3 N = normalize( normal ); vec3 L = normalize( light - position ); vec3 E = normalize( eye - position ); vec3 R = 2.*dot(L,N)*N - L; vec3 one = vec3( 1., 1., 1. ); gl_FragColor.rgb = diffuse(N,L)*color + .5*specular(N,L,E)*one + .5*fresnel(N,E)*one; gl_FragColor.a = 1.; } ================================================ FILE: Geodesics/shaders/vertex.glsl ================================================ varying vec3 position; varying vec3 normal; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_FrontColor = gl_Color; position = gl_Vertex.xyz; normal = gl_Normal.xyz; } ================================================ FILE: Geodesics/src/Camera.cpp ================================================ #include #include #include using namespace std; #include "Camera.h" namespace DDG { Camera :: Camera( void ) : pClick( 1. ), pDrag( 1. ), pLast( 1. ), rLast( 1. ), momentum( 1. ), zoom( 1. ) {} Quaternion Camera :: clickToSphere( int x, int y ) { GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); int w = viewport[2]; int h = viewport[3]; Quaternion p( 0., 2. * (double) x / (double) w - 1., 2. * (double) y / (double) h - 1., 0. ); if( p.norm2() > 1. ) { p.normalize(); p.im().z = 0.; } else { p.im().z = sqrt( 1. - p.norm2() ); } return p; } Quaternion Camera :: currentRotation( void ) const { return ( pDrag * pClick.conj() ) * rLast; } void Camera :: setView( void ) const { Quaternion r = ( pDrag * pClick.conj() ) * rLast; double w = r[0]; double x = r[1]; double y = r[2]; double z = r[3]; GLdouble M[16] = { 1.-2.*y*y-2.*z*z, 2.*x*y+2.*w*z, 2.*x*z-2.*w*y, 0., 2.*x*y-2.*w*z, 1.-2.*x*x-2.*z*z, 2.*y*z+2.*w*x, 0., 2.*x*z+2.*w*y, 2.*y*z-2.*w*x, 1.-2.*x*x-2.*y*y, 0., 0., 0., 0., 1. }; glMatrixMode( GL_MODELVIEW ); glMultMatrixd( M ); } void Camera :: mouse( int button, int state, int x, int y ) { if( state == GLUT_DOWN ) { pClick = pDrag = pLast = clickToSphere( x, y ); momentum = 1.; } if( state == GLUT_UP ) { double timeSinceDrag = ( clock() - tLast ) / (double) CLOCKS_PER_SEC; if( timeSinceDrag < .1 ) { momentum = pDrag * pLast.conj(); momentum = ( .03 * momentum + .97 ).unit(); } else { momentum = 1.; } rLast = pDrag * pClick.conj() * rLast; pClick = pDrag = 1.; } } void Camera :: motion( int x, int y ) { tLast = clock(); pLast = pDrag; pDrag = clickToSphere( x, y ); } void Camera :: idle( void ) { // get time since last idle event static int t0 = clock(); int t1 = clock(); double dt = (t1-t0) / (double) CLOCKS_PER_SEC; rLast = momentum * rLast; momentum = ( (1.-.5*dt) * momentum + .5*dt ).unit(); zoom += vZoom*dt; vZoom *= max( 0., 1.-5.*dt ); t0 = t1; } void Camera :: zoomIn( void ) { vZoom -= 0.5; } void Camera :: zoomOut( void ) { vZoom += 0.5; } } ================================================ FILE: Geodesics/src/Complex.cpp ================================================ #include "Complex.h" #include #include using namespace std; namespace DDG { Complex::Complex( double a, double b ) // constructs number a+bi : re( a ), im( b ) {} void Complex::operator+=( const Complex& z ) // add z { re += z.re; im += z.im; } void Complex::operator-=( const Complex& z ) // subtract z { re -= z.re; im -= z.im; } void Complex::operator*=( const Complex& z ) // Complex multiply by z { double a = re; double b = im; double c = z.re; double d = z.im; re = a*c-b*d; im = a*d+b*c; } void Complex::operator*=( double r ) // scalar multiply by r { re *= r; im *= r; } void Complex::operator/=( double r ) // scalar divide by r { re /= r; im /= r; } void Complex::operator/=( const Complex& z ) // scalar divide by r { *this *= z.inv(); } Complex Complex::operator-( void ) const { return Complex( -re, -im ); } Complex Complex::conj( void ) const // returns Complex conjugate { return Complex( re, -im ); } Complex Complex::inv( void ) const // returns inverse { return this->conj() / this->norm2(); } double Complex::arg( void ) const // returns argument { return atan2( im, re ); } double Complex::norm( void ) const // returns norm { return sqrt( re*re + im*im ); } double Complex::norm2( void ) const // returns norm squared { return re*re + im*im; } Complex Complex::unit( void ) const // returns complex number with unit norm and same modulus { return *this / this->norm(); } Complex Complex::exponential( void ) const // complex exponentiation { return exp( re ) * Complex( cos( im ), sin( im )); } Complex operator+( const Complex& z1, const Complex& z2 ) // binary addition { Complex z = z1; z += z2; return z; } Complex operator-( const Complex& z1, const Complex& z2 ) // binary subtraction { Complex z = z1; z -= z2; return z; } Complex operator*( const Complex& z1, const Complex& z2 ) // binary Complex multiplication { Complex z = z1; z *= z2; return z; } Complex operator*( const Complex& z, double r ) // right scalar multiplication { Complex zr = z; zr *= r; return zr; } Complex operator*( double r, const Complex& z ) // left scalar multiplication { return z*r; } Complex operator/( const Complex& z, double r ) // scalar division { Complex zr = z; zr /= r; return zr; } Complex operator/( const Complex& z1, const Complex& z2 ) // complex division { Complex z = z1; z /= z2; return z; } double dot( const Complex& z1, const Complex& z2 ) { return z1.re*z2.re + z1.im*z2.im; } double cross( const Complex& z1, const Complex& z2 ) { return z1.re*z2.im - z1.im*z2.re; } std::ostream& operator<<( std::ostream& os, const Complex& z ) // prints components { if( z.im > 0 ) { os << z.re << " + " << z.im << "i"; } else if( z.im < 0 ) { os << z.re << " - " << -z.im << "i"; } else { os << z.re; } return os; } } ================================================ FILE: Geodesics/src/DenseMatrix.cpp ================================================ #include "DenseMatrix.h" namespace DDG { template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i] = data[i]; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_COMPLEX, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i*2+0] = data[i].re; x[i*2+1] = data[i].im; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { assert( nColumns() == 1 ); if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } cData = cholmod_l_allocate_dense( m*4, 1, m*4, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { x[i*4+k] = data[i][k]; } } return cData; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = x[i]; } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = Complex( x[i*2+0], x[i*2+1] ); } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); assert( B->ncol == 1 ); assert( B->nrow%4 == 0 ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow/4; n = 1; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m; i++ ) { data[i] = Quaternion( x[i*4+0], x[i*4+1], x[i*4+2], x[i*4+3] ); } return *this; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 3; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { double x = o(i,j); if( x == 0. ) { os << " 0"; for( int k = 0; k < p+6; k++ ) { os << " "; } } else if( x > 0. ) { os << " " << x << " "; } else { os << x << " "; } } os << "]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { Complex z = o(i,j); if( z.re == 0. ) { os << " 0"; for( int k = 0; k < p+5; k++ ) { os << " "; } } else if( z.re > 0. ) { os << " " << z.re; } else { os << z.re; } if( z.im == 0 ) { os << " "; } else if( z.im >= 0 ) { os << "+"; } else { os << "-"; } if( z.im == 0. ) { for( int k = 0; k < p+8; k++ ) { os << " "; } } else { os << abs( z.im ) << "i "; } } os << " ]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "["; for( int j = 0; j < o.nColumns(); j++ ) { Quaternion q = o(i,j); os << " " << q; } os << " ]" << endl; } return os; } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i] = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i].re = 2.*unitRand() - 1.; data[i].im = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { data[i][k] = 2.*unitRand() - 1.; } } } } ================================================ FILE: Geodesics/src/DenseMatrix.inl ================================================ #include #include #include #include using namespace std; #include "DenseMatrix.h" #include "LinearContext.h" #include "Quaternion.h" #include "SparseMatrix.h" #include "Utility.h" namespace DDG { extern LinearContext context; template DenseMatrix :: DenseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) { data.resize( m*n ); zero(); } template DenseMatrix :: DenseMatrix( const DenseMatrix& A ) // copy constructor : cData( NULL ) { *this = A; } template DenseMatrix :: ~DenseMatrix( void ) // destructor { if( cData != NULL ) { cholmod_l_free_dense( &cData, context ); } } template DenseMatrix DenseMatrix :: transpose( void ) const { const DenseMatrix& A( *this ); DenseMatrix AT( n, m ); for( int i = 0; i < n; i++ ) for( int j = 0; j < m; j++ ) { AT(i,j) = A(j,i).conj(); } return AT; } template SparseMatrix DenseMatrix::sparse( void ) // converts to a sparse matrix { SparseMatrix B; B = cholmod_l_dense_to_sparse( this->to_cholmod(), true, context ); return B; } template DenseMatrix DenseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); DenseMatrix AB( A.nRows(), B.nColumns() ); for( int i = 0; i < A.nRows(); i++ ) for( int j = 0; j < B.nColumns(); j++ ) for( int k = 0; k < A.nColumns(); k++ ) { AB( i, j ) += A( i, k ) * B( k, j ); } return AB; } template void DenseMatrix :: operator*=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c; } } template void DenseMatrix :: operator/=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c.inv(); } } template DenseMatrix DenseMatrix :: operator+( const DenseMatrix& B ) const // returns sum of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) + B(i,j); } return C; } template void DenseMatrix :: operator+=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) += B(i,j); } } template DenseMatrix DenseMatrix :: operator-( const DenseMatrix& B ) const // returns difference of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) - B(i,j); } return C; } template void DenseMatrix :: operator-=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) -= B(i,j); } } template DenseMatrix operator*( const T& c, const DenseMatrix& A ) { DenseMatrix cA = A; cA *= c; return cA; } template DenseMatrix operator*( const DenseMatrix& A, double c ) { return c*A; } template DenseMatrix operator/( const DenseMatrix& A, double c ) { DenseMatrix Ac = A; Ac /= c; return Ac; } template const DenseMatrix& DenseMatrix :: operator=( const DenseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template int DenseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int DenseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int DenseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void DenseMatrix :: zero( const T& val ) // sets all elements to val { for( int i = 0; i < m*n; i++ ) { data[i] = val; } } template double DenseMatrix :: norm( NormType type ) const { double r = 0.; if( type == lInfinity ) { for( int i = 0; i < m*n; i++ ) { r = max( r, data[i].norm() ); } } else if( type == lOne ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm(); } } else if( type == lTwo ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm2(); } r = sqrt( r ); } return r; } template void DenseMatrix :: normalize( void ) // divides by l2 norm { *this /= norm( lTwo ); } template T& DenseMatrix :: operator()( int row, int col ) { return data[row+m*col]; } template T DenseMatrix :: operator()( int row, int col ) const { return data[row+m*col]; } template T& DenseMatrix :: operator()( int index ) { return data[index]; } template T DenseMatrix :: operator()( int index ) const { return data[index]; } template T DenseMatrix::sum( void ) const // returns the sum of all entries { T total( 0., 0. ); for( int i = 0; i < m*n; i++ ) { total += data[i]; } return total; } template void DenseMatrix :: removeMean( void ) { T mean = 0.; int N = m*n; for( int i = 0; i < N; i++ ) { mean += data[i]; } mean /= (double) N; for( int i = 0; i < N; i++ ) { data[i] -= mean; } } template T dot( const DenseMatrix& x, const DenseMatrix& y ) // returns Euclidean inner product of x and y { return ( x.transpose() * y )(0); } template DenseMatrix DenseMatrix::operator-( void ) const // returns additive inverse of this matrix { const DenseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { B( i, j ) = -A( i, j ); } return B; } template T inner( const DenseMatrix& x, const DenseMatrix& y ) // standard inner product { T sum = 0.; assert( x.nRows() == y.nRows() && x.nColumns() == y.nColumns() ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * y(i); } return sum; } template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ) // inner product with respect a diagonal inner // product B represented as a dense vector { T sum = 0.; assert( x.nRows() == y.nRows() && x.nRows() == B.nRows() && x.nColumns() == 1 && B.nColumns() == 1 && y.nColumns() == 1 ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * B(i) * y(i); } return sum; } } ================================================ FILE: Geodesics/src/DiscreteExteriorCalculus.inl ================================================ #include "DiscreteExteriorCalculus.h" namespace DDG { template void HodgeStar0Form :: build( const Mesh& mesh, SparseMatrix& star0 ) // builds a diagonal matrix mapping primal discrete 0-forms // to dual discrete 2-forms { int nV = mesh.vertices.size(); star0 = SparseMatrix( nV, nV ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { int i = v->index; star0( i, i ) = v->area(); } } template void HodgeStar1Form :: build( const Mesh& mesh, SparseMatrix& star1 ) // builds a diagonal matrix mapping primal discrete 1-forms // to dual discrete 1-forms { int nE = mesh.edges.size(); star1 = SparseMatrix( nE, nE ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // get the cotangents of the two angles opposite this edge double cotAlpha = e->he->cotan(); double cotBeta = e->he->flip->cotan(); int i = e->index; star1( i, i ) = ( cotAlpha + cotBeta ) / 2.; } } template void HodgeStar2Form :: build( const Mesh& mesh, SparseMatrix& star2 ) // builds a diagonal matrix mapping primal discrete 2-forms // to dual discrete 2-forms { int nF = mesh.faces.size(); star2 = SparseMatrix( nF, nF ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { int i = f->index; star2( i, i ) = 1. / f->area(); } } template< class T > void ExteriorDerivative0Form :: build( const Mesh& mesh, SparseMatrix& d0 ) { int nV = mesh.vertices.size(); int nE = mesh.edges.size(); d0 = SparseMatrix( nE, nV ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // the row index is the index of the edge int r = e->index; // the column indices are the indices of the two // edge vertices -- orientation is determined by // the orientation of the edge's first half edge int ci = e->he->vertex->index; int cj = e->he->flip->vertex->index; d0( r, ci ) = -1.; d0( r, cj ) = 1.; } } template< class T > void ExteriorDerivative1Form :: build( const Mesh& mesh, SparseMatrix& d1 ) { int nE = mesh.edges.size(); int nF = mesh.faces.size(); d1 = SparseMatrix( nF, nE ); // visit each face for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { // the row index is the index of the face int r = f->index; // visit all edges of this face HalfEdgeCIter he = f->he; do { // the column index is the index of the current edge int c = he->edge->index; // relative orientation is determined by checking if // the current half edge is the first half edge of its // corresponding edge double s = ( he->edge->he == he ? 1. : -1. ); // set the entry for this edge d1( r, c ) = s; he = he->next; } while( he != f->he ); } } } ================================================ FILE: Geodesics/src/Edge.cpp ================================================ #include "Edge.h" #include "Mesh.h" namespace DDG { } ================================================ FILE: Geodesics/src/Face.cpp ================================================ #include "Face.h" #include "Mesh.h" #include "Vector.h" namespace DDG { double Face::area( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).norm() / 2.; } Vector Face::normal( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).unit(); } bool Face::isBoundary( void ) const { return he->onBoundary; } Vector Face :: circumcenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector n = he->rotatedEdge(); double h = 0.5*he->cotan(); return 0.5*(p0+p1) + h*n; } Vector Face :: barycenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return (p0 + p1 + p2)/3.; } } ================================================ FILE: Geodesics/src/HalfEdge.cpp ================================================ #include "HalfEdge.h" #include "Mesh.h" namespace DDG { double HalfEdge :: cotan( void ) const { if( onBoundary ) return 0.0; Vector p0 = next->next->vertex->position; Vector p1 = vertex->position; Vector p2 = next->vertex->position; Vector u = p1-p0; Vector v = p2-p0; return dot( u, v ) / cross( u, v ).norm(); } Vector HalfEdge :: rotatedEdge( void ) const { if( onBoundary ) return Vector(); Vector n = face->normal(); Vector p0 = vertex->position; Vector p1 = flip->vertex->position; return cross( n, p1-p0 ); } } ================================================ FILE: Geodesics/src/Image.cpp ================================================ #include #include #include #include using namespace std; #include "Image.h" namespace DDG { Image :: Image( int width, int height ) : w( width ), h( height ), pixels( w*h*3 ) {} float& Image :: operator()( int x, int y ) // accesses pixel (x,y) { return pixels[ x + y*w ]; } const float& Image :: operator()( int x, int y ) const // accesses pixel (x,y) { return pixels[ x + y*w ]; } float Image :: sample( float x, float y ) const // samples image at (x,y) using bilinear filtering { const Image& I( *this ); float ax = x - floor( x ); float ay = y - floor( y ); float bx = 1. - ax; float by = 1. - ay; int x0 = (int) floor( x ); int y0 = (int) floor( y ); int x1 = x0 + 1; int y1 = y0 + 1; clamp( x0, y0 ); clamp( x1, y1 ); return by * ( bx * I(x0,y0) + ax * I(x1,y0) ) + ay * ( bx * I(x0,y1) + ax * I(x1,y1) ) ; } int Image :: width( void ) const // returns image width { return w; } int Image :: height( void ) const // returns image height { return h; } class TGAHeader // header format for Truevision TGA images { public: char idFieldSize; char colorMapType; char dataTypeCode; short colorMapOrigin; short colorMapLength; char colorMapEntrySize; short xOrigin; short yOrigin; short width; short height; char bitsPerPixel; char imageSpecification; }; void Image :: read( const char* filename ) // loads an image file in Truevision TGA format // (must be uncompressed RGB image with 24 or 32 bits per pixel) { ifstream in( filename, ios_base::binary ); if( !in.is_open() ) { cerr << "Error: could not open file " << filename << " for input!" << endl; exit( 1 ); } // read header TGAHeader header; in.read( (char*) &(header.idFieldSize), 1 ); in.read( (char*) &(header.colorMapType), 1 ); in.read( (char*) &(header.dataTypeCode), 1 ); in.read( (char*) &(header.colorMapOrigin), 2 ); in.read( (char*) &(header.colorMapLength), 2 ); in.read( (char*) &(header.colorMapEntrySize), 1 ); in.read( (char*) &(header.xOrigin), 2 ); in.read( (char*) &(header.yOrigin), 2 ); in.read( (char*) &(header.width), 2 ); in.read( (char*) &(header.height), 2 ); in.read( (char*) &(header.bitsPerPixel), 1 ); in.read( (char*) &(header.imageSpecification), 1 ); w = header.width; h = header.height; // validate data type const char uncompressedRGB = 2; if( header.dataTypeCode != uncompressedRGB || ( header.bitsPerPixel != 24 && header.bitsPerPixel != 32 )) { cerr << "Error: input must be uncompressed RGB image with 24 or 32 bits per pixel." << endl; exit( 1 ); } // read identification field (unused) vector idField( header.idFieldSize ); in.read( &idField[0], header.idFieldSize ); // read color map data (unused) if( header.colorMapType == 1 ) { int bytesPerEntry = header.colorMapEntrySize / 8; int colorMapSize = header.colorMapLength * bytesPerEntry; vector colorMapData( colorMapSize ); in.read( &colorMapData[0], colorMapSize ); } // read pixel data int n = w*h*header.bitsPerPixel/8; vector pixelData( n ); in.read( (char*) &pixelData[0], n ); // convert pixel data to floating point pixels.resize( n ); for( int i = 0; i < n; i++ ) { pixels[i] = (double) pixelData[i] / 255.; } } void Image :: write( const char* filename ) const // writes an image file in Truevision TGA format // (uncompressed RGB image with 24 bits per pixel) { ofstream out( filename, ios_base::binary ); if( !out.is_open() ) { cerr << "Error: could not open file " << filename << " for output!" << endl; exit( 1 ); } TGAHeader header; header.idFieldSize = 0; header.colorMapType = 0; header.dataTypeCode = 2; header.colorMapOrigin = 0; header.colorMapLength = 0; header.colorMapEntrySize = 0; header.xOrigin = 0; header.yOrigin = 0; header.width = w; header.height = h; header.bitsPerPixel = 24; header.imageSpecification = 0; // write header out.write( (char*) &(header.idFieldSize), 1 ); out.write( (char*) &(header.colorMapType), 1 ); out.write( (char*) &(header.dataTypeCode), 1 ); out.write( (char*) &(header.colorMapOrigin), 2 ); out.write( (char*) &(header.colorMapLength), 2 ); out.write( (char*) &(header.colorMapEntrySize), 1 ); out.write( (char*) &(header.xOrigin), 2 ); out.write( (char*) &(header.yOrigin), 2 ); out.write( (char*) &(header.width), 2 ); out.write( (char*) &(header.height), 2 ); out.write( (char*) &(header.bitsPerPixel), 1 ); out.write( (char*) &(header.imageSpecification), 1 ); // convert pixel data from floating point vector pixelData( w*h*3 ); for( int i = 0; i < w*h*3; i++ ) { pixelData[i] = (unsigned char)( pixels[i] * 255. ); } // write pixel data out.write( (char*) &pixelData[0], w*h*3 ); } void Image :: clamp( int& x, int& y ) const // clamps coordinates to range [0,w-1] x [0,h-1] { x = max( 0, min( w-1, x )); y = max( 0, min( h-1, y )); } } ================================================ FILE: Geodesics/src/LinearContext.cpp ================================================ #include "LinearContext.h" namespace DDG { // global context for linear solvers LinearContext context; LinearContext :: LinearContext( void ) // constructor { cholmod_l_start( &context ); } LinearContext :: ~LinearContext( void ) // destructor { cholmod_l_finish( &context ); } LinearContext :: operator cholmod_common*( void ) // allows LinearContext to be treated as a cholmod_common* { return &context; } } ================================================ FILE: Geodesics/src/LinearEquation.cpp ================================================ #include "LinearEquation.h" namespace DDG { LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ) // constructs a linear equation with the specified left- and right-hand side { LinearEquation eqn; eqn.lhs = lhs; eqn.rhs = rhs; return eqn; } } ================================================ FILE: Geodesics/src/LinearPolynomial.cpp ================================================ #include using namespace std; #include "LinearPolynomial.h" #include "Types.h" namespace DDG { LinearPolynomial :: LinearPolynomial( void ) : constantTerm( 0. ) {} LinearPolynomial :: LinearPolynomial( double c ) { *this = c; } LinearPolynomial :: LinearPolynomial( Variable& v ) { *this = v; } const LinearPolynomial& LinearPolynomial :: operator=( double c ) { linearTerms.clear(); constantTerm = c; return *this; } const LinearPolynomial& LinearPolynomial :: operator=( Variable& v ) { linearTerms.clear(); linearTerms[ &v ] = 1.; constantTerm = 0.; return *this; } void LinearPolynomial::operator+=( double c ) { constantTerm += c; } void LinearPolynomial::operator-=( double c ) { constantTerm -= c; } void LinearPolynomial::operator*=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second *= c; } constantTerm *= c; } void LinearPolynomial::operator/=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second /= c; } constantTerm /= c; } void LinearPolynomial::operator+=( Variable& v ) { LinearPolynomial p( v ); *this += p; } void LinearPolynomial::operator-=( Variable& v ) { LinearPolynomial p( v ); *this -= p; } void LinearPolynomial::operator+=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second += i->second; } else { linearTerms[i->first] = i->second; } } constantTerm += e.constantTerm; } void LinearPolynomial::operator-=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second -= i->second; } else { linearTerms[i->first] = -i->second; } } constantTerm -= e.constantTerm; } LinearPolynomial LinearPolynomial::operator-( void ) const { LinearPolynomial p = *this; for( TermIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { i->second = -i->second; } p.constantTerm = -p.constantTerm; return p; } double LinearPolynomial::evaluate( void ) const { double value = constantTerm; for( TermCIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { value += i->second * i->first->value; } return value; } LinearPolynomial operator+( double c, Variable& v ) { return c + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, double c ) { return LinearPolynomial(v) + c; } LinearPolynomial operator-( double c, Variable& v ) { return c - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, double c ) { return LinearPolynomial(v) - c; } LinearPolynomial operator*( double c, Variable& v ) { return LinearPolynomial(v) * c; } LinearPolynomial operator*( Variable& v, double c ) { return LinearPolynomial(v) * c; } LinearPolynomial operator/( Variable& v, double c ) { return LinearPolynomial(v) / c; } LinearPolynomial operator+( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) + LinearPolynomial(v2); } LinearPolynomial operator-( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) - LinearPolynomial(v2); } LinearPolynomial operator+( const LinearPolynomial& p, double c ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator+( double c, const LinearPolynomial& p ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, double c ) { LinearPolynomial difference = p; difference -= c; return difference; } LinearPolynomial operator-( double c, const LinearPolynomial& p ) { LinearPolynomial difference = -p; difference += c; return difference; } LinearPolynomial operator*( const LinearPolynomial& p, double c ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator*( double c, const LinearPolynomial& p ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator/( const LinearPolynomial& p, double c ) { LinearPolynomial quotient = p; quotient /= c; return quotient; } LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ) { return p + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) + p; } LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ) { return p - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) - p; } LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial sum = p; sum += q; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial difference = p; difference -= q; return difference; } ostream& operator<<( ostream& os, const LinearPolynomial& p ) { for( TermCIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { os << i->second << "*" << i->first->name << " + "; } os << p.constantTerm; return os; } } ================================================ FILE: Geodesics/src/LinearSystem.cpp ================================================ #include using namespace std; #include #include "LinearSystem.h" #include "LinearContext.h" #include "Types.h" namespace DDG { extern LinearContext context; void LinearSystem::clear( void ) // removes all equations from the system { equations.clear(); } void LinearSystem::push_back( const LinearEquation& e ) // appends the equation e to the sytem { equations.push_back( e ); } void LinearSystem::solve( void ) // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution { convertEquations(); indexVariables(); buildSparseMatrix(); buildRightHandSide(); computeSolution(); } void LinearSystem::convertEquations( void ) // converts each equation to its polynomial representation { currentEquations.clear(); for( EqnIter eqn = equations.begin(); eqn != equations.end(); eqn ++ ) { // move right-hand side to left-hand side LinearPolynomial p = eqn->lhs - eqn->rhs; // convert fixed variables to constants LinearPolynomial q( p.constantTerm ); for( TermIter t = p.linearTerms.begin(); t != p.linearTerms.end(); t ++ ) { const double& coefficient( t->second ); Variable& variable( *(t->first) ); // skip zeros if( coefficient == 0. ) continue; if( t->first->fixed ) { q += coefficient * variable.value; } else { q += coefficient * variable; } } if( q.linearTerms.size() > 0 ) { currentEquations.push_back( q ); } } nEquations = currentEquations.size(); } void LinearSystem::indexVariables( void ) // assign a unique index to each variable remaining in one of the polynomials { index.clear(); nVariables = 0; for( PolyCIter p = currentEquations.begin(); p != currentEquations.end(); p ++ ) { for( TermCIter t = p->linearTerms.begin(); t != p->linearTerms.end(); t ++ ) { IndexIter j = index.find( t->first ); // if we haven't seen this variable // before, assign it a unique index if( j == index.end() ) { index[ t->first ] = nVariables; nVariables++; } } } } void LinearSystem::buildSparseMatrix( void ) // build the sparse matrix representation of our current system { A = SparseMatrix( nEquations, nVariables ); for( int i = 0; i < nEquations; i++ ) { for( TermCIter t = currentEquations[i].linearTerms.begin(); t != currentEquations[i].linearTerms.end(); t ++ ) { int j = index[ t->first ]; A(i,j) = t->second; } } } void LinearSystem::buildRightHandSide( void ) // build the data vector for our current system { b = DenseMatrix( nEquations, 1 ); for( int i = 0; i < nEquations; i++ ) { b(i) = -currentEquations[i].constantTerm; } } void LinearSystem::computeSolution( void ) { // solve linear system Ax=b x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); // put solution values in variables for( IndexIter i = index.begin(); i != index.end(); i ++ ) { i->first->value = x( i->second ); } } } ================================================ FILE: Geodesics/src/Mesh.cpp ================================================ #include #include #include "Mesh.h" #include "MeshIO.h" #include "DiscreteExteriorCalculus.h" using namespace std; namespace DDG { Mesh :: Mesh( void ) {} Mesh :: Mesh( const Mesh& mesh ) { *this = mesh; } class HalfEdgeIterCompare { public: bool operator()( const HalfEdgeIter& i, const HalfEdgeIter& j ) const { return &*i < &*j; } }; class HalfEdgeCIterCompare { public: bool operator()( const HalfEdgeCIter& i, const HalfEdgeCIter& j ) const { return &*i < &*j; } }; class VertexIterCompare { public: bool operator()( const VertexIter& i, const VertexIter& j ) const { return &*i < &*j; } }; class VertexCIterCompare { public: bool operator()( const VertexCIter& i, const VertexCIter& j ) const { return &*i < &*j; } }; class FaceIterCompare { public: bool operator()( const FaceIter& i, const FaceIter& j ) const { return &*i < &*j; } }; class FaceCIterCompare { public: bool operator()( const FaceCIter& i, const FaceCIter& j ) const { return &*i < &*j; } }; class EdgeIterCompare { public: bool operator()( const EdgeIter& i, const EdgeIter& j ) const { return &*i < &*j; } }; class EdgeCIterCompare { public: bool operator()( const EdgeCIter& i, const EdgeCIter& j ) const { return &*i < &*j; } }; const Mesh& Mesh :: operator=( const Mesh& mesh ) { map< HalfEdgeCIter, HalfEdgeIter, HalfEdgeCIterCompare > halfedgeOldToNew; map< VertexCIter, VertexIter, VertexCIterCompare > vertexOldToNew; map< EdgeCIter, EdgeIter, EdgeCIterCompare > edgeOldToNew; map< FaceCIter, FaceIter, FaceCIterCompare > faceOldToNew; // copy geometry from the original mesh and create a // map from pointers in the original mesh to // those in the new mesh halfedges.clear(); for( HalfEdgeCIter he = mesh.halfedges.begin(); he != mesh.halfedges.end(); he++ ) halfedgeOldToNew[ he ] = halfedges.insert( halfedges.end(), *he ); vertices.clear(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) vertexOldToNew[ v ] = vertices.insert( vertices.end(), *v ); edges.clear(); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e++ ) edgeOldToNew[ e ] = edges.insert( edges.end(), *e ); faces.clear(); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) faceOldToNew[ f ] = faces.insert( faces.end(), *f ); // "search and replace" old pointers with new ones for( HalfEdgeIter he = halfedges.begin(); he != halfedges.end(); he++ ) { he->next = halfedgeOldToNew[ he->next ]; he->flip = halfedgeOldToNew[ he->flip ]; he->vertex = vertexOldToNew[ he->vertex ]; he->edge = edgeOldToNew[ he->edge ]; he->face = faceOldToNew[ he->face ]; } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) v->he = halfedgeOldToNew[ v->he ]; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) e->he = halfedgeOldToNew[ e->he ]; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) f->he = halfedgeOldToNew[ f->he ]; return *this; } int Mesh::read( const string& filename ) { inputFilename = filename; ifstream in( filename.c_str() ); if( !in.is_open() ) { cerr << "Error reading from mesh file " << filename << endl; return 1; } int rval; if( !( rval = MeshIO::read( in, *this ))) { indexElements(); normalize(); } return rval; } int Mesh::write( const string& filename ) const // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error { ofstream out( filename.c_str() ); if( !out.is_open() ) { cerr << "Error writing to mesh file " << filename << endl; return 1; } MeshIO::write( out, *this ); return 0; } bool Mesh::reload( void ) { return read( inputFilename ); } void Mesh::normalize( void ) { // compute center of mass Vector c( 0., 0., 0. ); for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { c += v->position; } c /= (double) vertices.size(); // translate to origin for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position -= c; } // rescale such that the mesh sits inside the unit ball double rMax = 0.; for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { rMax = max( rMax, v->position.norm() ); } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position /= rMax; } } void Mesh::indexElements( void ) { int nV = 0; for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->index = nV; nV++; } int nE = 0; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) { e->index = nE; nE++; } int nF = 0; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) { f->index = nF; nF++; } } double Mesh::area( void ) const { double sum = 0.0; for( FaceCIter f = faces.begin(); f != faces.end(); f ++ ) { sum += f->area(); } return sum; } double Mesh::meanEdgeLength( void ) const { double sum = 0; for( EdgeCIter e = edges.begin(); e != edges.end(); e ++) { VertexIter v0 = e->he->vertex; VertexIter v1 = e->he->flip->vertex; sum += (v0->position - v1->position).norm(); } return sum / edges.size(); } } ================================================ FILE: Geodesics/src/MeshIO.cpp ================================================ #include #include #include #include #include "MeshIO.h" #include "Mesh.h" using namespace std; namespace DDG { class Index { public: Index( void ) {} Index( int p, int t, int n ) : position( p ), texcoord( t ), normal( n ) {} bool operator<( const Index& i ) const { if( position < i.position ) return true; if( position > i.position ) return false; if( texcoord < i.texcoord ) return true; if( texcoord > i.texcoord ) return false; if( normal < i.normal ) return true; if( normal > i.normal ) return false; return false; } int position; int texcoord; int normal; }; class MeshData { public: std::vector positions; std::vector texcoords; std::vector normals; std::vector< std::vector< Index > > indices; }; int MeshIO :: read( istream& in, Mesh& mesh ) // reads a mesh from a valid, open input stream in { MeshData data; if( readMeshData( in, data )) { return 1; } if( buildMesh( data, mesh )) { return 1; } return 0; } void MeshIO :: write( ostream& out, const Mesh& mesh ) // writes a mesh to a valid, open output stream out { int currentIndex = 1; map vertexIndex; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) { out << "v " << v->position.x << " " << v->position.y << " " << v->position.z << endl; vertexIndex[ v ] = currentIndex; currentIndex++; } for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeIter he = f->he; for( int j = 0; j < 3; j++ ) { out << "vt " << he->texcoord.x << " " << he->texcoord.y << endl; he = he->next; } } for( size_t i = 0; i < mesh.faces.size(); i++ ) { const Face& f( mesh.faces[i] ); HalfEdgeIter he = f.he; out << "f "; int j = 0; do { out << vertexIndex[ he->vertex ] << "/" << 1+(i*3+j) << " "; he = he->next; j++; } while( he != f.he ); out << endl; } } int MeshIO :: readMeshData( istream& in, MeshData& data ) { string line; while( getline( in, line )) { stringstream ss( line ); string token; ss >> token; if( token == "v" ) { readPosition( ss, data ); continue; } // vertex if( token == "vt" ) { readTexCoord( ss, data ); continue; } // texture coordinate if( token == "vn" ) { readNormal ( ss, data ); continue; } // vertex normal if( token == "f" ) { readFace ( ss, data ); continue; } // face if( token[0] == '#' ) continue; // comment if( token == "o" ) continue; // object name if( token == "g" ) continue; // group name if( token == "s" ) continue; // smoothing group if( token == "mtllib" ) continue; // material library if( token == "usemtl" ) continue; // material if( token == "" ) continue; // empty string cerr << "Error: does not appear to be a valid Wavefront OBJ file!" << endl; cerr << "(Offending line: " << line << ")" << endl; return 1; } return 0; } void MeshIO :: preallocateMeshElements( const MeshData& data, Mesh& mesh ) { // count the number of edges set< pair > edges; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { for( unsigned int I = 0; I < f->size(); I++ ) { int J = (I+1) % f->size(); int i = (*f)[I].position; int j = (*f)[J].position; if( i > j ) swap( i, j ); edges.insert( pair( i, j )); } } int nV = data.positions.size(); int nE = edges.size(); int nF = data.indices.size(); int nHE = 2*nE; int chi = nV - nE + nF; int nB = max( 0, 2 - chi ); // (conservative approximation of number of boundary cycles) mesh.halfedges.clear(); mesh.vertices.clear(); mesh.edges.clear(); mesh.faces.clear(); mesh.boundaries.clear(); mesh.halfedges.reserve( nHE ); mesh.vertices.reserve( nV ); mesh.edges.reserve( nE ); mesh.faces.reserve( nF ); mesh.boundaries.reserve( nB ); } extern vector isolated; // all isolated vertices point to isolated.begin() int MeshIO :: buildMesh( const MeshData& data, Mesh& mesh ) { map< pair< int, int >, int > edgeCount; map< pair< int, int >, HalfEdgeIter > existingHalfEdges; map< int, VertexIter > indexToVertex; map< HalfEdgeIter, bool > hasFlipEdge; preallocateMeshElements( data, mesh ); // allocate a vertex for each position in the data and construct // a map from vertex indices to vertex pointers for( unsigned int i = 0; i < data.positions.size(); i++ ) { VertexIter newVertex = mesh.vertices.insert( mesh.vertices.end(), Vertex() ); newVertex->position = data.positions[ i ]; newVertex->he = isolated.begin(); indexToVertex[ i ] = newVertex; } // insert each face into the mesh int faceIndex = 0; bool degenerateFaces = false; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { int N = f->size(); // print an error if the face is degenerate if( N < 3 ) { cerr << "Error: face " << faceIndex << " is degenerate (fewer than three vertices)!" << endl; degenerateFaces = true; continue; } // create a new face FaceIter newFace = mesh.faces.insert( mesh.faces.end(), Face()); // create a new half edge for each edge of the current face vector< HalfEdgeIter > hes( N ); for( int i = 0; i < N; i++ ) { hes[ i ] = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); } // initialize these new halfedges for( int i = 0; i < N; i++ ) { // the current halfedge goes from vertex a to vertex b int a = (*f)[ i ].position; int b = (*f)[ (i+1) % N ].position; // set current halfedge's attributes hes[ i ]->next = hes[ (i+1) % N ]; hes[ i ]->vertex = indexToVertex[ a ]; int t = (*f)[i].texcoord; if( t >= 0 ) hes[ i ]->texcoord = data.texcoords[ t ]; else hes[ i ]->texcoord = Vector( 0., 0., 0. ); hes[ i ]->onBoundary = false; // keep track of which halfedges have flip edges defined (for detecting boundaries) hasFlipEdge[ hes[ i ]] = false; // point vertex a at the current halfedge indexToVertex[ a ]->he = hes[ i ]; // point the new face and this half edge to each-other hes[ i ]->face = newFace; newFace->he = hes[ i ]; // if we've created an edge between a and b in the past, it is the // flip edge of the current halfedge if( a > b ) swap( a, b ); if( existingHalfEdges.find( pair( a, b )) != existingHalfEdges.end()) { hes[ i ]->flip = existingHalfEdges[ pair( a, b ) ]; hes[ i ]->flip->flip = hes[ i ]; hes[ i ]->edge = hes[ i ]->flip->edge; hasFlipEdge[ hes[ i ]] = true; hasFlipEdge[ hes[ i ]->flip ] = true; } else // otherwise, create an edge connected to the current halfedge { hes[ i ]->edge = mesh.edges.insert( mesh.edges.end(), Edge()); hes[ i ]->edge->he = hes[i]; edgeCount[ pair( a, b ) ] = 0; } // record the fact that we've created a halfedge from a to b existingHalfEdges[ pair( a, b ) ] = hes[ i ]; // check for nonmanifold edges edgeCount[ pair( a, b ) ]++; if( edgeCount[ pair( a, b ) ] > 2 ) { cerr << "Error: edge (" << a << ", " << b << ") is nonmanifold (more than two faces sharing a single edge)!" << endl; return 1; } } faceIndex++; } // give up now if there were degenerate faces if( degenerateFaces ) { return 1; } // insert extra faces for each boundary cycle for( HalfEdgeIter currentHE = mesh.halfedges.begin(); currentHE != mesh.halfedges.end(); currentHE ++ ) { // if we find a halfedge with no flip edge defined, create // a new face and link it to the corresponding boundary cycle if( !hasFlipEdge[ currentHE ] ) { // create a new face FaceIter newBoundary = mesh.boundaries.insert( mesh.boundaries.end(), Face()); // walk along this boundary cycle vector boundaryCycle; HalfEdgeIter he = currentHE; do { // create a new halfedge on the boundary face HalfEdgeIter newHE = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); // mark only the halfedge on the boundary face as being on the boundary newHE->onBoundary = true; // link the current halfedge in the cycle to its new flip edge he->flip = newHE; // grab the next halfedge along the boundary by finding // the next halfedge around the current vertex that doesn't // have a flip edge defined HalfEdgeIter nextHE = he->next; while( hasFlipEdge[ nextHE ] ) { nextHE = nextHE->flip->next; } // set attributes for the flip edge (we'll set ->next below) newHE->flip = he; newHE->vertex = nextHE->vertex; newHE->edge = he->edge; newHE->face = newBoundary; newHE->texcoord = nextHE->texcoord; // point the new face to this half edge newBoundary->he = newHE; // keep track of all the new halfedges in the boundary cycle boundaryCycle.push_back( newHE ); // continue to walk along the cycle he = nextHE; } while( he != currentHE ); // link together the cycle of boundary halfedges unsigned int N = boundaryCycle.size(); for( unsigned int i = 0; i < N; i++ ) { boundaryCycle[ i ]->next = boundaryCycle[ (i+N-1)%N ]; hasFlipEdge[ boundaryCycle[i] ] = true; hasFlipEdge[ boundaryCycle[i]->flip ] = true; } } } // print a warning if the input has any non-terminal defects checkIsolatedVertices( mesh ); checkNonManifoldVertices( mesh ); return 0; } void MeshIO :: readPosition( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.positions.push_back( Vector( x, y, z )); } void MeshIO :: readTexCoord( stringstream& ss, MeshData& data ) { double u, v; ss >> u >> v; data.texcoords.push_back( Vector( u, v, 0. )); } void MeshIO :: readNormal( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.normals.push_back( Vector( x, y, z )); } void MeshIO :: readFace( stringstream& ss, MeshData &data ) { vector faceIndices; string token; while( ss >> token ) { faceIndices.push_back( parseFaceIndex( token )); } data.indices.push_back( faceIndices ); } Index MeshIO :: parseFaceIndex( const string& token ) { // parse indices of the form // // p/[t]/[n] // // where p is an index into positions, t is an index into // texcoords, n is an index into normals, and [.] indicates // that an index is optional stringstream in( token ); string indexstring; int indices[3] = { -1, -1, -1 }; int i = 0; while( getline( in, indexstring, '/' )) { stringstream ss( indexstring ); ss >> indices[i++]; } // decrement since indices in OBJ files are 1-based return Index( indices[0]-1, indices[1]-1, indices[2]-1 ); } void MeshIO :: checkIsolatedVertices( const Mesh& mesh ) { // print a warning if the mesh has any isolated vertices int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { cerr << "Warning: vertex " << vertexIndex << " is isolated (not contained in any face)." << endl; } vertexIndex++; } } void MeshIO :: checkNonManifoldVertices( const Mesh& mesh ) { map nIncidentFaces; for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } for( FaceCIter f = mesh.boundaries.begin(); f != mesh.boundaries.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( nIncidentFaces[v] != v->valence() ) { cerr << "Warning: vertex " << vertexIndex << " is nonmanifold." << endl; } vertexIndex++; } } } ================================================ FILE: Geodesics/src/Quaternion.cpp ================================================ #include #include using namespace std; #include "Quaternion.h" namespace DDG { // CONSTRUCTORS ---------------------------------------------------------- Quaternion :: Quaternion( void ) // initializes all components to zero : s( 0. ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Quaternion& q ) // initializes from existing quaternion : s( q.s ), v( q.v ) {} Quaternion :: Quaternion( double s_, double vi, double vj, double vk ) // initializes with specified double (s) and imaginary (v) components : s( s_ ), v( vi, vj, vk ) {} Quaternion :: Quaternion( double s_, const Vector& v_ ) // initializes with specified double(s) and imaginary (v) components : s( s_ ), v( v_ ) {} Quaternion :: Quaternion( double s_ ) // initializes purely real quaternion with specified real (s) component (imaginary part is zero) : s( s_ ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Vector& v_ ) // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) : s( 0. ), v( v_ ) {} Quaternion :: Quaternion( const Complex& z ) // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k : s( z.re ), v( z.im, 0., 0. ) {} // ASSIGNMENT OPERATORS -------------------------------------------------- const Quaternion& Quaternion :: operator=( double _s ) // assigns a purely real quaternion with real value s { s = _s; v = Vector( 0., 0., 0. ); return *this; } const Quaternion& Quaternion :: operator=( const Vector& _v ) // assigns a purely real quaternion with imaginary value v { s = 0.; v = _v; return *this; } // ACCESSORS ------------------------------------------------------------- double& Quaternion::operator[]( int index ) // returns reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } const double& Quaternion::operator[]( int index ) const // returns const reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } void Quaternion::toMatrix( double Q[4][4] ) const // returns 4x4 matrix representation { Q[0][0] = s; Q[0][1] = -v.x; Q[0][2] = -v.y; Q[0][3] = -v.z; Q[1][0] = v.x; Q[1][1] = s; Q[1][2] = -v.z; Q[1][3] = v.y; Q[2][0] = v.y; Q[2][1] = v.z; Q[2][2] = s; Q[2][3] = -v.x; Q[3][0] = v.z; Q[3][1] = -v.y; Q[3][2] = v.x; Q[3][3] = s; } double& Quaternion::re( void ) // returns reference to double part { return s; } const double& Quaternion::re( void ) const // returns const reference to double part { return s; } Vector& Quaternion::im( void ) // returns reference to imaginary part { return v; } const Vector& Quaternion::im( void ) const // returns const reference to imaginary part { return v; } // VECTOR SPACE OPERATIONS ----------------------------------------------- Quaternion Quaternion::operator+( const Quaternion& q ) const // addition { return Quaternion( s+q.s, v+q.v ); } Quaternion Quaternion::operator-( const Quaternion& q ) const // subtraction { return Quaternion( s-q.s, v-q.v ); } Quaternion Quaternion::operator-( void ) const // negation { return Quaternion( -s, -v ); } Quaternion Quaternion::operator*( double c ) const // scalar multiplication { return Quaternion( s*c, v*c ); } Quaternion operator*( double c, const Quaternion& q ) // scalar multiplication { return q*c; } Quaternion Quaternion::operator/( double c ) const // scalar division { return Quaternion( s/c, v/c ); } void Quaternion::operator+=( const Quaternion& q ) // addition / assignment { s += q.s; v += q.v; } void Quaternion::operator+=( double c ) // addition / assignment of pure real { s += c; } void Quaternion::operator-=( const Quaternion& q ) // subtraction / assignment { s -= q.s; v -= q.v; } void Quaternion::operator-=( double c ) // subtraction / assignment of pure real { s -= c; } void Quaternion::operator*=( double c ) // scalar multiplication / assignment { s *= c; v *= c; } void Quaternion::operator/=( double c ) // scalar division / assignment { s /= c; v /= c; } // ALGEBRAIC OPERATIONS -------------------------------------------------- Quaternion Quaternion::operator*( const Quaternion& q ) const // Hamilton product { const double& s1( s ); const double& s2( q.s ); const Vector& v1( v ); const Vector& v2( q.v ); return Quaternion( s1*s2 - dot(v1,v2), s1*v2 + s2*v1 + cross(v1,v2) ); } void Quaternion::operator*=( const Quaternion& q ) // Hamilton product / assignment { *this = ( *this * q ); } Quaternion Quaternion::conj( void ) const // conjugation { return Quaternion( s, -v ); } Quaternion Quaternion::inv( void ) const { return ( this->conj() ) / this->norm2(); } // NORMS ----------------------------------------------------------------- double Quaternion::norm( void ) const // returns Euclidean length { return sqrt( s*s + v.x*v.x + v.y*v.y + v.z*v.z ); } double Quaternion::norm2( void ) const // returns Euclidean length squared { return s*s + dot(v,v); } Quaternion Quaternion::unit( void ) const // returns unit quaternion { return *this / norm(); } void Quaternion::normalize( void ) // divides by Euclidean length { *this /= norm(); } // GEOMETRIC OPERATIONS -------------------------------------------------- Quaternion slerp( const Quaternion& q0, const Quaternion& q1, double t ) // spherical-linear interpolation { // interpolate length double m0 = q0.norm(); double m1 = q1.norm(); double m = (1-t)*m0 + t*m1; // interpolate direction Quaternion p0 = q0 / m0; Quaternion p1 = q1 / m1; double theta = acos(( p0.conj()*p1 ).re() ); Quaternion p = ( sin((1-t)*theta)*p0 + sin(t*theta)*p1 )/sin(theta); return m*p; } // I/O ------------------------------------------------------------------------- std::ostream& operator<<( std::ostream& os, const Quaternion& q ) // prints components { os << "( " << q.re() << ", " << q.im() << " )"; return os; } } ================================================ FILE: Geodesics/src/Real.cpp ================================================ #include "Real.h" #include namespace DDG { Real :: Real( double x ) // constructs real number with value x : value( x ) {} Real :: operator double( void ) const // type cast to double { return value; } void Real :: operator+=( double x ) // increment { value += x; } void Real :: operator-=( double x ) // decrement { value -= x; } void Real :: operator*=( double x ) // multiply { value *= x; } void Real :: operator/=( double x ) // divide { value /= x; } Real Real :: conj( void ) const // simply returns the value (for compatibility w/ complex numbers) { return value; } Real Real :: inv( void ) const // returns inverse { return 1. / value; } double Real :: norm( void ) const // returns norm { return fabs( value ); } double Real :: norm2( void ) const // returns norm squared { return value * value; } Real Real :: unit( void ) const // returns number with unit norm and same sign { return value / norm(); } } ================================================ FILE: Geodesics/src/Shader.cpp ================================================ #include "Shader.h" #include #include using namespace std; namespace DDG { Shader::Shader( void ) // constructor -- shader is initially invalid : vertexShader( 0 ), fragmentShader( 0 ), geometryShader( 0 ), program( 0 ), linked( false ) {} Shader::~Shader( void ) { if( program ) glDeleteProgram( program ); if( vertexShader ) glDeleteShader( vertexShader ); if( fragmentShader ) glDeleteShader( fragmentShader ); if( geometryShader ) glDeleteShader( geometryShader ); } void Shader::loadVertex( const char* filename ) { load( GL_VERTEX_SHADER, filename, vertexShader ); } void Shader::loadFragment( const char* filename ) { load( GL_FRAGMENT_SHADER, filename, fragmentShader ); } void Shader::loadGeometry( const char* filename ) { #ifdef GL_GEOMETRY_SHADER_EXT load( GL_GEOMETRY_SHADER_EXT, filename, geometryShader ); #else cerr << "Error: geometry shaders not supported!" << endl; #endif } void Shader::enable( void ) { if( !linked ) { glLinkProgram( program ); linked = true; } glUseProgram( program ); } void Shader::disable( void ) const { glUseProgram( 0 ); } Shader::operator GLuint( void ) const { return program; } void Shader::load( GLenum shaderType, const char* filename, GLuint& shader ) // read vertex shader from GLSL source file, compile, and attach to program { string source; if( !readSource( filename, source )) { return; } if( program == 0 ) { program = glCreateProgram(); } if( shader != 0 ) { glDetachShader( program, shader ); } shader = glCreateShader( shaderType ); const char* source_c_str = source.c_str(); glShaderSource( shader, 1, &(source_c_str), NULL ); glCompileShader( shader ); GLint compileStatus; glGetShaderiv( shader, GL_COMPILE_STATUS, &compileStatus ); if( compileStatus == GL_TRUE ) { glAttachShader( program, shader ); linked = false; } else { GLsizei maxLength = 0; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength ); if( maxLength > 0 ) { GLchar* infoLog = new char[ maxLength ]; GLsizei length; glGetShaderInfoLog( shader, maxLength, &length, infoLog ); cerr << "GLSL Error: " << infoLog << endl; delete[] infoLog; } } } bool Shader::readSource( const char* filename, std::string& source ) // reads GLSL source file into a string { source = ""; ifstream in( filename ); if( !in.is_open() ) { cerr << "Error: could not open shader file "; cerr << filename; cerr << " for input!" << endl; return false; } string line; while( getline( in, line )) { source += line; } return true; } } ================================================ FILE: Geodesics/src/SparseMatrix.cpp ================================================ #include "SparseMatrix.h" namespace DDG { template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = pr[k]; } } return *this; } template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = Complex( pr[k*2+0], pr[k*2+1] ); } } return *this; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ) { SparseMatrix A( m*4, n*4 ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; const Quaternion& q( e->second ); A(i*4+0,j*4+0) = q[0]; A(i*4+0,j*4+1) = -q[1]; A(i*4+0,j*4+2) = -q[2]; A(i*4+0,j*4+3) = -q[3]; A(i*4+1,j*4+0) = q[1]; A(i*4+1,j*4+1) = q[0]; A(i*4+1,j*4+2) = -q[3]; A(i*4+1,j*4+3) = q[2]; A(i*4+2,j*4+0) = q[2]; A(i*4+2,j*4+1) = q[3]; A(i*4+2,j*4+2) = q[0]; A(i*4+2,j*4+3) = -q[1]; A(i*4+3,j*4+0) = q[3]; A(i*4+3,j*4+1) = -q[2]; A(i*4+3,j*4+2) = q[1]; A(i*4+3,j*4+3) = q[0]; } if( cData != NULL ) { cholmod_l_free_sparse( &cData, context ); } cData = cholmod_l_copy_sparse( A.to_cholmod(), context ); return cData; } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_COMPLEX, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m*4, n*4, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i] = e->second; } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i*2+0] = e->second.re; pr[i*2+1] = e->second.im; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR< complex >( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (complex)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (quaternion)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4]/4 << "\n"; } template <> void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_zl_symbolic( n, n, Ap, Ai, Ax, NULL, &Symbolic, NULL, NULL ); umfpack_zl_numeric( Ap, Ai, Ax, NULL, Symbolic, &Numeric, NULL, NULL ); umfpack_zl_solve( UMFPACK_A, Ap, Ai, Ax, NULL, (double*) &x(0), NULL, (double*) &b(0), NULL, Numeric, NULL, NULL ); umfpack_zl_free_symbolic( &Symbolic ); umfpack_zl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } } ================================================ FILE: Geodesics/src/SparseMatrix.inl ================================================ #include #include #include #include #include using namespace std; #include #include #include "Real.h" #include "Complex.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "LinearContext.h" #include "Utility.h" namespace DDG { extern LinearContext context; const int maxEigIter = 20; // number of iterations used to solve eigenvalue problems template SparseMatrix :: SparseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) {} template SparseMatrix :: SparseMatrix( const SparseMatrix& B ) // copy constructor : cData( NULL ) { *this = B; } template SparseMatrix :: ~SparseMatrix( void ) // destructor { if( cData ) { cholmod_l_free_sparse( &cData, context ); } } template const SparseMatrix& SparseMatrix :: operator=( const SparseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template SparseMatrix SparseMatrix :: transpose( void ) const { SparseMatrix AT( n, m ); for( const_iterator e = begin(); e != end(); e++ ) { int i = e->first.second; int j = e->first.first; T Aij = e->second; AT(j,i) = Aij.conj(); } return AT; } template SparseMatrix SparseMatrix :: operator*( const SparseMatrix& B ) const // returns product of this matrix with sparse B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // collect nonzeros in each row vector< vector< int > > Bcol( B.nRows() ); vector< vector< T > > Bval( B.nRows() ); for( const_iterator e = B.begin(); e != B.end(); e ++ ) { int row = e->first.second; int col = e->first.first; T val = e->second; Bcol[ row ].push_back( col ); Bval[ row ].push_back( val ); } // multiply C = A*B SparseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( size_t n = 0; n < Bcol[j].size(); n++ ) { int k = Bcol[j][n]; C( i, k ) += e->second * Bval[j][n]; } } return C; } template DenseMatrix SparseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with dense B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // multiply C = A*B DenseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( int k = 0; k < B.nColumns(); k++ ) { C( i, k ) += e->second * B( j, k ); } } return C; } template void SparseMatrix :: operator*=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second *= c; } } template void SparseMatrix :: operator/=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second /= c; } } template void SparseMatrix :: operator+=( const SparseMatrix& B ) // adds B to this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) += Bij; } } template void SparseMatrix :: operator-=( const SparseMatrix& B ) // subtracts B from this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) -= Bij; } } template SparseMatrix SparseMatrix :: operator+( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C += B; return C; } template SparseMatrix SparseMatrix :: operator-( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C -= B; return C; } template SparseMatrix operator*( const T& c, const SparseMatrix& A ) { SparseMatrix cA = A; for( typename SparseMatrix::iterator e = cA.begin(); e != cA.end(); e++ ) { e->second = c * e->second; } return cA; } template SparseMatrix operator*( const SparseMatrix& A, const T& c ) { SparseMatrix Ac = A; Ac *= c; return Ac; } template SparseMatrix operator/( const SparseMatrix& A, T c ) { SparseMatrix Ac = A; Ac /= c; return Ac; } template void SparseMatrix :: resize( int m_, int n_ ) { m = m_; n = n_; data.clear(); } template int SparseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int SparseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int SparseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void SparseMatrix :: zero( const T& val ) // sets all nonzero elements val { for( iterator i = begin(); i != end(); i++ ) { i->second = val; } } template SparseMatrix SparseMatrix :: inverse( void ) const // returns inverse -- for diagonal matrices only { assert( m == n ); // matrix must be square const SparseMatrix& A( *this ); SparseMatrix Ainv( m, m ); for( const_iterator e = begin(); e != end(); e++ ) { int r = e->first.second; int c = e->first.first; assert( r == c ); // matrix must be diagonal Ainv( r, c ) = A( r, c ).inv(); } return Ainv; } template SparseMatrix SparseMatrix :: identity( int N ) { SparseMatrix I( N, N ); for( int i = 0; i < N; i++ ) { I( i, i ) = 1.; } return I; } template DenseMatrix SparseMatrix :: full( void ) const // converts to a dense matrix { const int maxSize = 1048576; if( m*n > maxSize ) { cerr << "Error: refusing to convert sparse to dense (too big!)" << "\n"; exit( 1 ); } const SparseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < m; j++ ) { B( i, j ) = A( i, j ); } return B; } template cholmod_sparse* SparseMatrix :: to_cholmod( void ) { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } allocateSparse(); // build compressed matrix (note that EntryMap stores entries in column-major order) double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; int i = 0; int j = -1; for( const_iterator e = begin(); e != end(); e ++ ) { int c = e->first.first; if( c != j ) { for( int k = j+1; k <= c; k++ ) { jc[k] = i; } j = c; } ir[i] = e->first.second; setEntry( e, i, pr ); i++; } for( int k = j+1; k <= n; k++ ) { jc[k] = i; } return cData; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ); template T& SparseMatrix :: operator()( int row, int col ) { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { data[ index ] = T( 0. ); } return data[ index ]; } template T SparseMatrix :: operator()( int row, int col ) const { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { return T( 0. ); } return entry->second; } template typename SparseMatrix::iterator SparseMatrix :: begin( void ) { return data.begin(); } template typename SparseMatrix::const_iterator SparseMatrix :: begin( void ) const { return data.begin(); } template typename SparseMatrix::iterator SparseMatrix :: end( void ) { return data.end(); } template typename SparseMatrix::const_iterator SparseMatrix :: end( void ) const { return data.end(); } template void SparseMatrix :: shift( double c ) // adds c times the identity matrix to this matrix { assert( m == n ); SparseMatrix& A( *this ); for( int i = 0; i < m; i++ ) { A( i, i ) += c; } } template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_dl_symbolic( n, n, Ap, Ai, Ax, &Symbolic, NULL, NULL ); umfpack_dl_numeric( Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL ); umfpack_dl_solve( UMFPACK_A, Ap, Ai, Ax, (double*) &x(0), (double*) &b(0), Numeric, NULL, NULL ); umfpack_dl_free_symbolic( &Symbolic ); umfpack_dl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; cholmod_factor* L = cholmod_l_analyze( Ac, context ); cholmod_l_factorize( Ac, L, context ); x = cholmod_l_solve( CHOLMOD_A, L, b.to_cholmod(), context ); if( L ) cholmod_l_free_factor( &L, context ); int t1 = clock(); cout << "[chol] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[chol] max residual: " << residual( A, x, b ) << "\n"; } template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ) // backsolves the prefactored positive definite sparse linear system LL'x = b { x = cholmod_l_solve( CHOLMOD_A, L.to_cholmod(), b.to_cholmod(), context ); } template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess { int t0 = clock(); for( int iter = 0; iter < maxEigIter; iter++ ) { solve( A, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess { // TODO use a symmetric matrix decomposition instead of QR int t0 = clock(); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= dot( e, B*e ).norm(); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; solve( A, x, x ); x -= dot( x, Be ).conj()*e; x /= dot( x, B*x ).norm(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); for( int iter = 0; iter < maxEigIter; iter++ ) { backsolvePositiveDefinite( L, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= sqrt( dot( e, B*e ).norm() ); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; backsolvePositiveDefinite( L, x, x ); x -= dot( x, Be ).conj()*e; x /= sqrt( dot( x, B*x ).norm() ); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ) // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess { int iter; int t0 = clock(); DenseMatrix ET = E.transpose(); SparseFactor L; L.build( A ); for( iter = 0; iter < maxEigIter; iter++ ) { x = B*x - E*(ET*x); backsolvePositiveDefinite( L, x, x ); x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, E, x ) << "\n"; } template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ) // returns the max residual of the linear problem A x = b relative to the largest entry of the solution { return ( A*x - b ).norm() / b.norm(); } template double residual( const SparseMatrix& A, const DenseMatrix& x ) // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, x ); return (A*x-lambda*x).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, x ); return (A*x-lambda*(B*x)).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, E, x ); return (A*x-lambda*(B*x-E*(E.transpose()*x))).norm() / x.norm(); } template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*x)(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x))(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns /<(B-EE^T)x,x> { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x-E*(E.transpose()*x)))(0).inv(); } template std::ostream& operator<<( std::ostream& os, const SparseMatrix& o) { os.precision( 3 ); for( typename SparseMatrix::const_iterator e = o.begin(); e != o.end(); e ++ ) { int row = e->first.second; int col = e->first.first; os << "( " << row << ", " << col << " ): " << e->second << "\n"; } return os; } template SparseFactor :: SparseFactor( void ) : L( NULL ) {} template SparseFactor :: ~SparseFactor( void ) { if( L ) { cholmod_l_free_factor( &L, context ); } } template void SparseFactor :: build( SparseMatrix& A ) { if( L ) { cholmod_l_free_factor( &L, context ); L = NULL; } int t0, t1; cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; t0 = clock(); L = cholmod_l_analyze( Ac, context ); t1 = clock(); cerr << "analyze: " << seconds(t0,t1) << "s" << endl; t0 = clock(); cholmod_l_factorize( Ac, L, context ); t1 = clock(); cerr << "factorize: " << seconds(t0,t1) << "s" << endl; } template bool SparseFactor :: valid( void ) const { if( L == NULL ) { return false; } return true; } template cholmod_factor* SparseFactor :: to_cholmod( void ) { return L; } } ================================================ FILE: Geodesics/src/Variable.cpp ================================================ #include "Variable.h" namespace DDG { Variable :: Variable( double value_, bool fixed_ ) // initialize a variable which has value zero and is not fixed by default : value( value_ ), fixed( fixed_ ) {} Variable :: Variable( std::string name_, double value_, bool fixed_ ) // initialize a named variable which has value zero and is not fixed by default : name( name_ ), value( value_ ), fixed( fixed_ ) {} double& Variable :: operator*( void ) // returns a reference to the numerical value { return value; } const double& Variable :: operator*( void ) const // returns a const reference to the numerical value { return value; } } ================================================ FILE: Geodesics/src/Vector.cpp ================================================ #include #include "Vector.h" namespace DDG { Vector :: Vector( void ) : x( 0. ), y( 0. ), z( 0. ) {} Vector :: Vector( double x0, double y0, double z0 ) : x( x0 ), y( y0 ), z( z0 ) {} Vector :: Vector( const Vector& v ) : x( v.x ), y( v.y ), z( v.z ) {} double& Vector :: operator[]( const int& index ) { return ( &x )[ index ]; } const double& Vector :: operator[]( const int& index ) const { return ( &x )[ index ]; } Vector Vector :: operator+( const Vector& v ) const { return Vector( x + v.x, y + v.y, z + v.z ); } Vector Vector :: operator-( const Vector& v ) const { return Vector( x - v.x, y - v.y, z - v.z ); } Vector Vector :: operator-( void ) const { return Vector( -x, -y, -z ); } Vector Vector :: operator*( const double& c ) const { return Vector( x*c, y*c, z*c ); } Vector operator*( const double& c, const Vector& v ) { return v*c; } Vector Vector :: operator/( const double& c ) const { return (*this) * ( 1./c ); } void Vector :: operator+=( const Vector& v ) { x += v.x; y += v.y; z += v.z; } void Vector :: operator-=( const Vector& v ) { x -= v.x; y -= v.y; z -= v.z; } void Vector :: operator*=( const double& c ) { x *= c; y *= c; z *= c; } void Vector :: operator/=( const double& c ) { (*this) *= ( 1./c ); } double Vector :: norm( void ) const { return sqrt( norm2()); } double Vector :: norm2( void ) const { return dot( *this, *this ); } void Vector :: normalize( void ) { (*this) /= norm(); } Vector Vector :: unit( void ) const { return (*this) / norm(); } Vector Vector :: abs( void ) const { return Vector( fabs( x ), fabs( y ), fabs( z ) ); } double dot( const Vector& u, const Vector& v ) { return u.x*v.x + u.y*v.y + u.z*v.z ; } Vector cross( const Vector& u, const Vector& v ) { return Vector( u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x ); } std::ostream& operator << (std::ostream& os, const Vector& o) { os << "[ " << o.x << " " << o.y << " " << o.z << " ]"; return os; } } ================================================ FILE: Geodesics/src/Vertex.cpp ================================================ #include using namespace std; #include "Vertex.h" #include "Mesh.h" #include "HalfEdge.h" namespace DDG { double Vertex::area( void ) const // returns the dual area associated with this vertex { double A = 0.; HalfEdgeCIter h = he; do { if (not h->onBoundary) A += h->face->area(); h = h->flip->next; } while( h != he ); return A / 3.; } Vector Vertex::normal( void ) const // returns the vertex normal { Vector N; HalfEdgeCIter h = he; do { if (not h->onBoundary) N += h->face->normal(); h = h->flip->next; } while( h != he ); return N.unit(); } vector isolated; // all isolated vertices point to isolated.begin() bool Vertex::isIsolated( void ) const // returns true if the vertex is not contained in any face or edge; false otherwise { return he == isolated.begin(); } int Vertex :: valence( void ) const // returns the number of incident faces { int n = 0; HalfEdgeCIter h = he; do { n++; h = h->flip->next; } while( h != he ); return n; } void Vertex :: toggleTag() { tag = !tag; } } ================================================ FILE: Geodesics/src/Viewer.cpp ================================================ #include #include #include #include #include #include #include using namespace std; #include "Viewer.h" #include "Image.h" #include "Application.h" namespace DDG { // declare static member variables Mesh Viewer::mesh; GLuint Viewer::surfaceDL = 0; int Viewer::windowSize[2] = { 512, 512 }; Camera Viewer::camera; Shader Viewer::shader; bool Viewer::renderWireframe = false; bool Viewer::renderVectorField = false; double Viewer::maxDistance = 1.0; double Viewer::step = 1.0; double Viewer::delta = 0.1; void Viewer :: init( void ) { restoreViewerState(); initGLUT(); setGL(); initGLSL(); updateDisplayList(); glutMainLoop(); } void Viewer :: initGLUT( void ) { int argc = 0; vector< vector > argv(1); // initialize window glutInitWindowSize( windowSize[0], windowSize[1] ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); glutInit( &argc, (char**)&argv ); glutCreateWindow( "DDG" ); // specify callbacks glutDisplayFunc ( Viewer::display ); glutIdleFunc ( Viewer::idle ); glutKeyboardFunc ( Viewer::keyboard ); glutSpecialFunc ( Viewer::special ); glutMouseFunc ( Viewer::mouse ); glutMotionFunc ( Viewer::motion ); // initialize menus int viewMenu = glutCreateMenu( Viewer::view ); glutSetMenu( viewMenu ); glutAddMenuEntry( "[f] Wireframe", menuWireframe ); glutAddMenuEntry( "[↑] Zoom In", menuZoomIn ); glutAddMenuEntry( "[↓] Zoom Out", menuZoomOut ); glutAddMenuEntry( "[-] DecreaseStep", menuDecreaseStep ); glutAddMenuEntry( "[=] IncreaseStep", menuIncreaseStep ); glutAddMenuEntry( "[v] VectorField", menuVectorField ); int mainMenu = glutCreateMenu( Viewer::menu ); glutSetMenu( mainMenu ); glutAddMenuEntry( "[space] Process Mesh", menuProcess ); glutAddMenuEntry( "[r] Reset Mesh", menuResetMesh ); glutAddMenuEntry( "[w] Write Mesh", menuWriteMesh ); glutAddMenuEntry( "[\\] Screenshot", menuScreenshot ); glutAddMenuEntry( "[esc] Exit", menuExit ); glutAddSubMenu( "View", viewMenu ); glutAttachMenu( GLUT_RIGHT_BUTTON ); } void Viewer :: initGLSL( void ) { shader.loadVertex( "shaders/vertex.glsl" ); shader.loadFragment( "shaders/fragment.glsl" ); } void Viewer :: menu( int value ) { switch( value ) { case( menuProcess ): mProcess(); break; case( menuResetMesh ): mResetMesh(); break; case( menuWriteMesh ): mWriteMesh(); break; case( menuScreenshot ): mScreenshot(); break; case( menuExit ): mExit(); break; case( menuIncreaseStep ): mIncreaseStep(); break; case( menuDecreaseStep ): mDecreaseStep(); break; default: break; } } void Viewer :: view( int value ) { switch( value ) { case( menuWireframe ): mWireframe(); break; case( menuZoomIn ): mZoomIn(); break; case( menuZoomOut ): mZoomOut(); break; case( menuVectorField ): mVectorField(); break; default: break; } } void Viewer :: keyboard( unsigned char c, int x, int y ) { switch( c ) { case 'f': mWireframe(); break; case 'w': mWriteMesh(); break; case 'r': mResetMesh(); break; case '\\': mScreenshot(); break; case ' ': mProcess(); break; case 27: mExit(); break; case '-': mDecreaseStep(); break; case '=': mIncreaseStep(); break; case 'v': mVectorField(); break; default: break; } } void Viewer :: special( int i, int x, int y ) { switch( i ) { case GLUT_KEY_UP: camera.zoomIn(); break; case GLUT_KEY_DOWN: camera.zoomOut(); break; case 27: mExit(); break; default: break; } } void Viewer :: mouse( int button, int state, int x, int y ) { if( ( glutGetModifiers() & GLUT_ACTIVE_SHIFT) and state == GLUT_UP ) pickVertex(x, y); else camera.mouse( button, state, x, y ); } void Viewer :: motion( int x, int y ) { camera.motion( x, y ); } void Viewer :: idle( void ) { camera.idle(); glutPostRedisplay(); } void Viewer :: storeViewerState( void ) { ofstream out( ".viewer_state.txt" ); out << camera.rLast[0] << endl; out << camera.rLast[1] << endl; out << camera.rLast[2] << endl; out << camera.rLast[3] << endl; GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); out << view[2] << endl; out << view[3] << endl; } void Viewer :: restoreViewerState( void ) { ifstream in( ".viewer_state.txt" ); if( !in.is_open() ) return; in >> camera.rLast[0]; in >> camera.rLast[1]; in >> camera.rLast[2]; in >> camera.rLast[3]; in >> windowSize[0]; in >> windowSize[1]; } void Viewer :: mIncreaseStep( void ) { step += delta; std::cout << "Step = " << step << std::endl; } void Viewer :: mDecreaseStep( void ) { step -= delta; std::cout << "Step = " << step << std::endl; } void Viewer :: mProcess( void ) { Application app; maxDistance = app.run(step, mesh); std::cout << "MaxDist = " << maxDistance << std::endl; updateDisplayList(); } void Viewer :: mResetMesh( void ) { mesh.reload(); updateDisplayList(); } void Viewer :: mWriteMesh( void ) { mesh.write( "out.obj" ); } void Viewer :: mExit( void ) { //storeViewerState(); exit( 0 ); } void Viewer :: mWireframe( void ) { renderWireframe = !renderWireframe; updateDisplayList(); } void Viewer :: mVectorField( void ) { renderVectorField = !renderVectorField; updateDisplayList(); } void Viewer :: mZoomIn( void ) { camera.zoomIn(); } void Viewer :: mZoomOut( void ) { camera.zoomOut(); } void Viewer :: mScreenshot( void ) { static int index = 0; // get window width and height GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); int w = view[2]; int h = view[3]; // get pixels Image image( w, h ); glReadPixels( 0, 0, w, h, GL_BGR, GL_FLOAT, &image(0,0) ); stringstream filename; filename << "frames/viewer" << setw(8) << setfill( '0' ) << index << ".tga"; image.write( filename.str().c_str() ); index++; } void Viewer :: display( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); shader.enable(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); double aspect = (double) viewport[2] / (double) viewport[3]; const double fovy = 50.; const double clipNear = .01; const double clipFar = 1000.; gluPerspective( fovy, aspect, clipNear, clipFar ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); Quaternion eye = Vector( 0., 0., -2.5*camera.zoom ); Quaternion center = Vector( 0., 0., 0. ); Quaternion up = Vector( 0., 1., 0. ); gluLookAt( eye[1], eye[2], eye[3], center[1], center[2], center[3], up[1], up[2], up[3] ); Quaternion r = camera.currentRotation(); eye = r.conj() * eye * r; GLint uniformEye = glGetUniformLocation( shader, "eye" ); glUniform3f( uniformEye, eye[1], eye[2], eye[3] ); Quaternion light = Vector( -1., 1., -2. ); light = r.conj() * light * r; GLint uniformLight = glGetUniformLocation( shader, "light" ); glUniform3f( uniformLight, light[1], light[2], light[3] ); camera.setView(); callDisplayList(); shader.disable(); glutSwapBuffers(); } void Viewer :: updateDisplayList( void ) { if( surfaceDL ) { glDeleteLists( surfaceDL, 1 ); surfaceDL = 0; } surfaceDL = glGenLists( 1 ); glNewList( surfaceDL, GL_COMPILE ); setMeshMaterial(); drawScene(); glEndList(); } void Viewer :: setGL( void ) { glClearColor( .5, .5, .5, 1. ); setLighting(); } void Viewer :: setLighting( void ) { GLfloat position[4] = { 20., 30., 40., 0. }; glLightfv( GL_LIGHT0, GL_POSITION, position ); glEnable( GL_LIGHT0 ); glEnable( GL_NORMALIZE ); } void Viewer :: setMeshMaterial( void ) { GLfloat diffuse[4] = { .8, .5, .3, 1. }; GLfloat specular[4] = { .3, .3, .3, 1. }; GLfloat ambient[4] = { .2, .2, .5, 1. }; glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular ); glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient ); glMaterialf ( GL_FRONT_AND_BACK, GL_SHININESS, 16. ); } void Viewer :: callDisplayList( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_DEPTH_TEST ); glEnable( GL_LIGHTING ); glCallList( surfaceDL ); glPopAttrib(); } void Viewer :: drawScene( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1., 1. ); drawPolygons(); glDisable( GL_POLYGON_OFFSET_FILL ); if( renderWireframe ) drawWireframe(); if( renderVectorField ) drawVectorField(); drawIsolatedVertices(); drawSelectedVertices(); glPopAttrib(); } void Viewer :: drawPolygons( void ) { glEnable(GL_COLOR_MATERIAL); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { if( f->isBoundary() ) continue; glBegin( GL_POLYGON ); if( renderWireframe ) { Vector N = f->normal(); glNormal3dv( &N[0] ); } HalfEdgeCIter he = f->he; do { if( not renderWireframe ) { Vector N = he->vertex->normal(); glNormal3dv( &N[0] ); } double alpha = he->vertex->distance / maxDistance; glColor4f( alpha, alpha, alpha, 1. ); glVertex3dv( &he->vertex->position[0] ); he = he->next; } while( he != f->he ); glEnd(); } glDisable(GL_COLOR_MATERIAL); } void Viewer :: drawWireframe( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glDisable( GL_LIGHTING ); glColor4f( 0., 0., 0., 0.5 ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBegin( GL_LINES ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { glVertex3dv( &e->he->vertex->position[0] ); glVertex3dv( &e->he->flip->vertex->position[0] ); } glEnd(); glPopAttrib(); } void Viewer :: drawIsolatedVertices( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glPointSize( 5 ); glHint( GL_POINT_SMOOTH_HINT, GL_NICEST ); glEnable( GL_POINT_SMOOTH ); glColor3f( 1., 0., 0. ); glBegin( GL_POINTS ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { glVertex3dv( &v->position[0] ); } } glEnd(); glPopAttrib(); } void Viewer :: drawVertices( void ) { for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { glLoadName(v->index); glBegin(GL_POINTS); glVertex3dv( &v->position[0] ); glEnd(); } } void Viewer :: drawSelectedVertices( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_COLOR_MATERIAL ); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glColor3f( 0., 0.5, 0.5 ); double h = 0.75*mesh.meanEdgeLength(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->tag ) { glPushMatrix(); glTranslated(v->position.x, v->position.y, v->position.z); glutSolidSphere(h, 10, 10); glPopMatrix(); } } glEnd(); glPopAttrib(); } void Viewer :: drawVectorField( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); double h = 0.25*mesh.meanEdgeLength(); glDisable( GL_LIGHTING ); glColor3f( 0., 0., 0. ); glLineWidth( 2.0 ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { if( f->isBoundary() ) continue; Vector a = f->barycenter(); Vector b = a + h*f->vector; Vector n = f->normal(); Vector v = b - a; Vector v90 = cross(n, v); Vector p0 = b; Vector p1 = p0 - 0.2 * v - 0.1 * v90; Vector p2 = p0 - 0.2 * v + 0.1 * v90; glBegin( GL_LINES ); glVertex3dv( &a[0] ); glVertex3dv( &b[0] ); glEnd(); glBegin(GL_TRIANGLES); glVertex3dv( &p0[0] ); glVertex3dv( &p1[0] ); glVertex3dv( &p2[0] ); glEnd(); } glPopAttrib(); } void Viewer :: pickVertex(int x, int y) { int width = glutGet(GLUT_WINDOW_WIDTH ); int height = glutGet(GLUT_WINDOW_HEIGHT); if( x < 0 || x >= width || y < 0 || y >= height ) return; int bufSize = mesh.vertices.size(); GLuint* buf = new GLuint[bufSize]; glSelectBuffer(bufSize, buf); GLint viewport[4]; GLdouble projection[16]; glGetIntegerv( GL_VIEWPORT, viewport ); glGetDoublev(GL_PROJECTION_MATRIX, projection); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix(x, viewport[3]-y, 10, 10, viewport); glMultMatrixd(projection); glMatrixMode(GL_MODELVIEW); glPushMatrix(); drawVertices(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); long hits = glRenderMode(GL_RENDER); int index = -1; double min_z = 1.0e100; for( long i = 0; i < hits; ++i ) { double distance = buf[4*i + 1]; if( distance < min_z ) { index = buf[4*i + 3]; min_z = distance; } } delete[] buf; if (index >= 0) { mesh.vertices[index].toggleTag(); updateDisplayList(); } } } ================================================ FILE: Geodesics/src/main.cpp ================================================ #include using namespace std; #include "Viewer.h" #include "DenseMatrix.h" using namespace DDG; int main( int argc, char** argv ) { if( argc != 2 ) { cerr << "usage: " << argv[0] << " in.obj" << endl; return 1; } Viewer viewer; viewer.mesh.read( argv[1] ); viewer.init(); return 0; } ================================================ FILE: HOWTO ================================================ ================================================================================= DGP-DEC Siggraph Course 2013 ================================================================================= DGP-DEC is a framework for writing digital geometry processing (DGP) applications. It provides a simple and practical interface for typical geometry operations based on Discrete Exterior Calculus (DEC). ////////////// // BUILDING // ////////////// 0) cd 1) Comment/uncomment the header flags for your OS 2) make 3) ./ //////////////////// // VIEWER CONTROL // //////////////////// Mouse: * left_click: rotate * right_click: show menu * shift + left_click: pick/unpick vertex Shortcuts: * w: save mesh * f: wireframe on/off * space: run application * Apple/Ctrl+r: reload mesh * -/=: decrease/increase time step ////////////////// // DEPENDENCIES // ////////////////// * GLUT * GLSL * Tim Davis' SuiteSparseQR sparse QR factorization library: http://www.cise.ufl.edu/research/sparse/SPQR/ which in turn depends on SuiteSparse and METIS: http://www.cise.ufl.edu/research/sparse/SuiteSparse/ http://glaros.dtc.umn.edu/gkhome/views/metis as well as some (hopefully optimized!) BLAS/LAPACK implementation. On UNIX-like systems you will probably end up needing the libraries bamd.a libcamd.a libcolamd.a libccolamd.a libcholmod.a libspqr.a from SuiteSparse and libmetis.a from METIS. If you want to avoid compiling all of SuiteSparse, you can simply type "make" in each of the appropriate Lib directories (e.g., AMD/Lib) after setting up UFConfig and copying the resulting library (.a) files to /usr/local/bin or some other appropriate place. Further instructions on building SuiteSparse and its dependencies can be found on the SuiteSparse home page. On Mac OS X, the easiest way to link to an efficient BLAS implementation is by adding the framework -framework Accelerate On other platforms, Kazushige Goto's GotoBLAS library is a popular choice: http://www.tacc.utexas.edu/tacc-projects/gotoblas2/ ////////////////////// // WRITING NEW APPS // ////////////////////// 1) Add code for your application in "include/Application.h". 2) Call Application inside Viewer::mProcess() in "src/Viewer.cpp". 3) Customize any other class according to your application. * * Copyright 2010 Keenan Crane,Fernando de Goes,Mathieu Desbrun, Peter Schroder. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those * of the author and should not be interpreted as representing official policies, * either expressed or implied, of any other person or institution. * ================================================ FILE: Hot2/.viewer_state.txt ================================================ 0.126663 -0.0243652 -0.989174 0.0699843 815 767 ================================================ FILE: Hot2/Makefile ================================================ ########################################################################################## # Specify library locations here (add or remove "#" marks to comment/uncomment lines for your platform) # Mac OS X DDG_INCLUDE_PATH = DDG_LIBRARY_PATH = DDG_BLAS_LIBS = -framework Accelerate DDG_SUITESPARSE_LIBS = -lspqr -lumfpack -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -ltbb -lm -lsuitesparseconfig DDG_OPENGL_LIBS = -framework OpenGL -framework GLUT # # Linux # DDG_INCLUDE_PATH = # DDG_LIBRARY_PATH = # DDG_BLAS_LIBS = -llapack -lblas -lgfortran # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut -lGL -lGLU -lX11 # # Windows / Cygwin # DDG_INCLUDE_PATH = -I/usr/include/opengl -I/usr/include/suitesparse # DDG_LIBRARY_PATH = -L/usr/lib/w32api -L/usr/lib/suitesparse # DDG_BLAS_LIBS = -llapack -lblas # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lcolamd -lccolamd -lcamd -lamd -lm # DDG_OPENGL_LIBS = -lglut32 -lglu32 -lopengl32 ######################################################################################## TARGET = hot2 CC = g++ LD = g++ CFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_INCLUDE_PATH) -I./include -I./src LFLAGS = -O3 -Wall -Werror -ansi -pedantic $(DDG_LIBRARY_PATH) LIBS = $(DDG_OPENGL_LIBS) $(DDG_SUITESPARSE_LIBS) $(DDG_BLAS_LIBS) ######################################################################################## ## !! Do not edit below this line HEADERS := $(wildcard include/*.h) SOURCES := $(wildcard src/*.cpp) OBJECTS := $(addprefix obj/,$(notdir $(SOURCES:.cpp=.o))) all: $(TARGET) $(TARGET): $(OBJECTS) $(LD) $(OBJECTS) -o $(TARGET) $(CFLAGS) $(LFLAGS) $(LIBS) obj/%.o: src/%.cpp ${HEADERS} $(CC) -c $< -o $@ $(CFLAGS) clean: rm -f $(OBJECTS) rm -f $(TARGET) rm -f $(TARGET).exe ================================================ FILE: Hot2/include/Application.h ================================================ /* * HOT: Hodge-Optimized Triangulations * Patrick Mullen, Pooran Memari, Fernando de Goes, Mathieu Desbrun. * Transactions on Graphics (SIGGRAPH) 2011. */ #ifndef DDG_APPLICATION_H #define DDG_APPLICATION_H #include "Mesh.h" #include "DenseMatrix.h" #include "SparseMatrix.h" #include "DiscreteExteriorCalculus.h" namespace DDG { class Application { public: void optimizeWeights(Mesh& mesh) { SparseMatrix d0, star0, star1, Delta; HodgeStar1Form::build( mesh, star1 ); HodgeStar0Form::build( mesh, star0 ); ExteriorDerivative0Form::build( mesh, d0 ); Delta = d0.transpose() * star1 * d0; Delta += Real(1e-8)*star0; DenseMatrix rhs; buildRhs(mesh, rhs); DenseMatrix x; solvePositiveDefinite(Delta, x, rhs); assignSolution(mesh, x); } protected: void buildRhs(const Mesh& mesh, DenseMatrix& rhs) const { rhs = DenseMatrix(mesh.vertices.size(),1); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++) { double sum = 0.0; HalfEdgeIter he = v->he; do { if( !he->onBoundary ) { Vector n = he->next->rotatedEdge(); Vector c = he->face->circumcenter(); Vector b = he->face->barycenter(); sum += dot( n, c-b ); } he = he->flip->next; } while( he != v->he ); rhs(v->index,0) = sum; } } void assignSolution(Mesh& mesh, const DenseMatrix& x) const { for( VertexIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++) { v->weight = x(v->index,0); } } }; } #endif ================================================ FILE: Hot2/include/Camera.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Camera.h // ----------------------------------------------------------------------------- // // Camera is used by Viewer to keep track of the view state; it also // handles mouse input related to camera manipulation. // #ifndef DDG_CAMERA_H #define DDG_CAMERA_H #include "Quaternion.h" #ifdef __CYGWIN__ #define GLUT_DISABLE_ATEXIT_HACK #include #include #include #include #else #include #endif namespace DDG { class Camera { public: Camera( void ); // constructor Quaternion clickToSphere( int x, int y ); // projects a mous click onto the unit sphere void setView( void ) const; // applies the camera transformation to the OpenGL modelview stack void mouse( int button, int state, int x, int y ); // handles mouse clicks void motion( int x, int y ); // handles mouse drags void idle( void ); // handles camera momentum void zoomIn( void ); // moves viewer toward object void zoomOut( void ); // moves viewer away from object Quaternion currentRotation( void ) const; // returns the rotation corresponding to the current mouse state Quaternion pClick; // mouse coordinates of current click Quaternion pDrag; // mouse coordinates of current drag Quaternion pLast; // mouse coordinates of previous drag Quaternion rLast; // previous camera rotation Quaternion momentum; // camera momentum int tLast; // time of previous drag double zoom, vZoom; // zoom and zoom velocity }; } #endif ================================================ FILE: Hot2/include/Complex.h ================================================ #ifndef DDG_COMPLEX_H #define DDG_COMPLEX_H #include namespace DDG { class Complex { public: Complex( double a=0., double b=0. ); // constructs number a+bi void operator+=( const Complex& z ); // add z void operator-=( const Complex& z ); // subtract z void operator*=( const Complex& z ); // Complex multiply by z void operator*=( double r ); // scalar multiply by r void operator/=( double r ); // scalar divide by r void operator/=( const Complex& z ); // complex divide by r Complex operator-( void ) const; // returns the additive inverse Complex conj( void ) const; // returns Complex conjugate Complex inv( void ) const; // returns inverse double arg( void ) const; // returns argument double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Complex unit( void ) const; // returns complex number with unit norm and same modulus Complex exponential( void ) const; // complex exponentiation double re; // real part double im; // imaginary part }; Complex operator+( const Complex& z1, const Complex& z2 ); // binary addition Complex operator-( const Complex& z1, const Complex& z2 ); // binary subtraction Complex operator*( const Complex& z1, const Complex& z2 ); // binary Complex multiplication Complex operator*( const Complex& z, double r ); // right scalar multiplication Complex operator*( double r, const Complex& z ); // left scalar multiplication Complex operator/( const Complex& z, double r ); // scalar division Complex operator/( const Complex& z1, const Complex& z2 ); // complex division double dot( const Complex& z1, const Complex& z2 ); // inner product double cross( const Complex& z1, const Complex& z2 ); // cross product std::ostream& operator<<( std::ostream& os, const Complex& o ); // prints components } #endif ================================================ FILE: Hot2/include/DenseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DenseMatrix.h // ----------------------------------------------------------------------------- // // DenseMatrix represents an m by n (real or complex) matrix where every // entry -- including zero-valued entries -- is stored explicitly. This // class is most commonly used to represent dense vectors in sparse linear // systems (i.e., the right hand side and the solution vector). // // A real or complex matrix is allocated via // // DenseMatrix A( m, n ); // DenseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // DenseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a DenseMatrix returns a cholmod_dense* // which can be used by routines in SuiteSparse. For basic operations, however, // you should not need to access this pointer explicitly -- see the solve() // method in SparseMatrix.h. // #ifndef DDG_DENSEMATRIX_H #define DDG_DENSEMATRIX_H #include #include "Types.h" #include namespace DDG { enum NormType { lInfinity, lOne, lTwo }; template class DenseMatrix { public: DenseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix (specifying just m yields a column vector) DenseMatrix( const DenseMatrix& A ); // copy constructor const DenseMatrix& operator=( const DenseMatrix& B ); // copies B ~DenseMatrix( void ); // destructor SparseMatrix sparse( void ); // converts to a sparse matrix int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val = 0. ); // sets all elements to val double norm( NormType type = lInfinity ) const; // returns the maximum magnitude of any entry T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element of the matrix (uses 0-based indexing) T& operator()( int index ); T operator()( int index ) const; // access the specified element of a vector (uses 0-based indexing) DenseMatrix transpose( void ) const; // returns the transpose of this matrix DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c DenseMatrix operator+( const DenseMatrix& B ) const; // returns sum of this matrix with B void operator+=( const DenseMatrix& B ); // adds B to this matrix DenseMatrix operator-( const DenseMatrix& B ) const; // returns difference of this matrix with B void operator-=( const DenseMatrix& B ); // subtracts B from this matrix DenseMatrix operator-( void ) const; // returns additive inverse of this matrix cholmod_dense* to_cholmod( void ); // returns pointer to copy of matrix in CHOLMOD format const DenseMatrix& operator=( cholmod_dense* B ); // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B void normalize( void ); // divides by Frobenius norm T sum( void ) const; // returns the sum of all entries void removeMean( void ); // removes the mean void randomize( void ); // replaces entries with uniformly distributed random real numbers in the interval [-1,1] protected: int m, n; std::vector data; cholmod_dense* cData; }; template DenseMatrix operator*( const DenseMatrix& A, const T& c ); // right scalar multiplication template DenseMatrix operator*( const T& c, const DenseMatrix& A ); // left scalar multiplication template DenseMatrix operator/( const DenseMatrix& A, const T& c ); // scalar division template T dot( const DenseMatrix& x, const DenseMatrix& y ); // returns Euclidean inner product of x and y template std::ostream& operator << (std::ostream& os, const DenseMatrix& o); // prints entries template T inner( const DenseMatrix& x, const DenseMatrix& y ); // standard inner product template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ); // inner product with respect to a diagonal inner // product B represented as a dense vector } #include "DenseMatrix.inl" #endif ================================================ FILE: Hot2/include/DiscreteExteriorCalculus.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- DiscreteExteriorCalculus.h // ----------------------------------------------------------------------------- // // Static methods for building the fundamental discrete operators (exterior // derivative, Hodge star) for 0-, 1-, and 2-forms on a surface mesh. Methods // are templated on entry type, i.e., one can build either real- or complex- // matrices using the types DDG::Real and DDG::Complex, respectively. For // instance, to build the usual Laplacian on functions, one could write // // Mesh mesh; // SparseMatrix d0, star0, star1, Delta; // // ExteriorDerivative0Form::build( mesh, d0 ); // HodgeStar0Form::build( mesh, star0 ); // HodgeStar1Form::build( mesh, star1 ); // Delta = star0.inverse() * d0.transpose() * star1 * d0; // #ifndef DDG_DISCRETEEXTERIORCALCULUS_H #define DDG_DISCRETEEXTERIORCALCULUS_H #include "Mesh.h" #include "SparseMatrix.h" namespace DDG { template< class T > struct HodgeStar0Form { static void build( const Mesh& mesh, SparseMatrix& star0 ); }; template< class T > struct HodgeStar1Form { static void build( const Mesh& mesh, SparseMatrix& star1 ); }; template< class T > struct HodgeStar2Form { static void build( const Mesh& mesh, SparseMatrix& star2 ); }; template< class T > struct ExteriorDerivative0Form { static void build( const Mesh& mesh, SparseMatrix& d0 ); }; template< class T > struct ExteriorDerivative1Form { static void build( const Mesh& mesh, SparseMatrix& d1 ); }; } #include "DiscreteExteriorCalculus.inl" #endif ================================================ FILE: Hot2/include/Edge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Edge.h // ----------------------------------------------------------------------------- // // Edge stores attributes associated with a mesh edge. The iterator he points // to one of its two associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_EDGE_H #define DDG_EDGE_H #include "Types.h" namespace DDG { class Edge { public: HalfEdgeIter he; // points to one of the two halfedges associated with this edge int index; // unique integer ID in the range 0, ..., nEdges-1 Edge() : index(0) { } Vector dualPoint() const; // intersection between primal and dual edge }; } #endif ================================================ FILE: Hot2/include/Face.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Face.h // ----------------------------------------------------------------------------- // // Face stores attributes associated with a mesh edge. The iterator he points // to one of its associated halfedges. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_FACE_H #define DDG_FACE_H #include "Types.h" namespace DDG { class Face { public: HalfEdgeIter he; // points to one of the halfedges associated with this face int index; // unique integer ID in the range 0, ..., nFaces-1 Face() : index(0) { } bool isBoundary( void ) const; // returns true if this face corresponds to a // boundary loop; false otherwise double area( void ) const; // returns the triangle area Vector normal( void ) const; // returns the unit normal associated with this face; normal // orientation is determined by the circulation order of halfedges Vector circumcenter( void ) const; // returns triangle circumcenter Vector barycenter( void ) const; // returns triangle barycenter Vector dualPoint( void ) const; // returns dual vertex from weighted triangulation }; } #endif ================================================ FILE: Hot2/include/HalfEdge.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- HalfEdge.h // ----------------------------------------------------------------------------- // // HalfEdge is used to define mesh connectivity. (See the documentation for a // more in-depth discussion of the halfedge data structure.) // #ifndef DDG_HALFEDGE_H #define DDG_HALFEDGE_H #include "Vector.h" #include "Types.h" namespace DDG { class HalfEdge { public: HalfEdgeIter next; // points to the next halfedge around the current face HalfEdgeIter flip; // points to the other halfedge associated with this edge VertexIter vertex; // points to the vertex at the "tail" of this halfedge EdgeIter edge; // points to the edge associated with this halfedge FaceIter face; // points to the face containing this halfedge bool onBoundary; // true if this halfedge is contained in a boundary // loop; false otherwise Vector texcoord; // texture coordinates associated with the triangle corner at the // "tail" of this halfedge double cotan( void ) const; // returns the cotangent of the angle opposing this edge Vector rotatedEdge( void ) const; // returns oriented edge vector rotated by PI/2 around face normal // if onBoundary, then return nil double height( void ) const; // returns signed distance from dual vertex to orthogonal projection double shift( void ) const; // returns signed distance from he->vertex to dual vertex projection }; } #endif ================================================ FILE: Hot2/include/Image.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Image.h // ----------------------------------------------------------------------------- // // Image represents a color bitmap image. A simple example might look like // // Image im; // im.read( "input.tga" ); // // modify image data via im(x,y) = ...; // im.write( "output.tga" ); // #ifndef DDG_IMAGE_H #define DDG_IMAGE_H #include #include namespace DDG { class Image { public: Image( int width = 0, int height = 0 ); // constructs image with specified width and height float& operator()( int x, int y ); const float& operator()( int x, int y ) const; // accesses pixel (x,y) float sample( float x, float y ) const; // samples image at (x,y) using bilinear filtering int width( void ) const; int height( void ) const; // returns image dimensions void read( const char* filename ); // loads an image file in Truevision TGA format // (must be RGB image with 24 or 32 bits per pixel) void write( const char* filename ) const; // writes an image file in Truevision TGA format // (RGB image with 24 bits per pixel) protected: void clamp( int& x, int& y ) const; // clamps coordinates to range [0,w-1] x [0,h-1] int w, h; // width and height std::vector pixels; // interleaved RGBA pixel data in range [0-1] }; } #endif ================================================ FILE: Hot2/include/LinearContext.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearContext.h // ----------------------------------------------------------------------------- // // LinearContext is the global solver context needed to interface with the // SuiteSparse library. It is essentially a wrapper around cholmod_common. A // single static instance of LinearContext is declared in LinearContext.cpp and // is shared by all instances of DenseMatrix, SparseMatrix, and LinearSystem. // In other words, you shouldn't have to instantiate LinearContext yourself // unless you're doing something really fancy! // #ifndef DDG_LINEARSOLVERCONTEXT #define DDG_LINEARSOLVERCONTEXT #include namespace DDG { class LinearContext { public: LinearContext( void ); // constructor ~LinearContext( void ); // destructor operator cholmod_common*( void ); // allows LinearContext to be treated as a cholmod_common* protected: cholmod_common context; }; } #endif ================================================ FILE: Hot2/include/LinearEquation.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearEquation.h // ----------------------------------------------------------------------------- // // LinearEquation represents an equation with an arbitrary linear polynomial on // both the left- and right-hand side. It is primarily used while building a // LinearSystem. For convenience, operator== is overloaded so that the user // can construct a LinearEquation by writing something that looks much like the // usual mathematical syntax for a linear equation. For example, // // LinearEquation eqn = ( x + 2*y == 3*z ); // // builds the linear equation x + 2y = 3z. // #ifndef DDG_LINEAREQUATION_H #define DDG_LINEAREQUATION_H #include "LinearPolynomial.h" namespace DDG { class LinearEquation { public: LinearPolynomial lhs; // left-hand side LinearPolynomial rhs; // right-hand side }; LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ); // constructs a linear equation with the specified left- and right-hand side } #endif ================================================ FILE: Hot2/include/LinearPolynomial.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearPolynomial.h // ----------------------------------------------------------------------------- // // LinearPolynomial represents an affine function of the form // // f(x1,x2,...,xn) = c1 x1 + c2 x2 + ... + cn xn + d // // where the xi are real-valued variables with real coefficients ci, and d is // a real constant. The variables and their coefficients are represented using // instances of the Variable class. LinearPolynomial implements all the usual // algebraic operations on affine functions, as well as type conversions from // more elementary types (scalars, single variables, etc.). // // Importantly, variables used in a LinearPolynomial should *not* be deallocated // while the polynomial is still in use -- LinearPolynomial stores only a // reference to these variables so that the solution to a linear system can be // automatically copied back into the variables. // #ifndef DDG_LINEARPOLYNOMIAL_H #define DDG_LINEARPOLYNOMIAL_H #include #include #include "Variable.h" namespace DDG { class LinearPolynomial { public: LinearPolynomial( void ); // constructs the zero function LinearPolynomial( double c ); // constructs the constant function with value c LinearPolynomial( Variable& v ); // constructs a function with a single variable v const LinearPolynomial& operator=( double c ); // assigns the constant function with value c const LinearPolynomial& operator=( Variable& v ); // assigns a function with a single variable v void operator+=( double c ); void operator-=( double c ); void operator*=( double c ); void operator/=( double c ); // adds, subtract, multiplies, or divides by a constant void operator+=( Variable& v ); void operator-=( Variable& v ); // increments or decrements by a single variable v void operator+=( const LinearPolynomial& p ); void operator-=( const LinearPolynomial& p ); // increments or decrements by an affine function LinearPolynomial operator-( void ) const; // returns the additive inverse (i.e., negation) double evaluate( void ) const; // evaluates the function using the current values of its variables std::map linearTerms; // list of linear terms double constantTerm; // constant term }; LinearPolynomial operator+( double c, Variable& v ); // sum LinearPolynomial operator+( Variable& v, double c ); // sum LinearPolynomial operator-( double c, Variable& v ); // difference LinearPolynomial operator-( Variable& v, double c ); // difference LinearPolynomial operator*( double c, Variable& v ); // product LinearPolynomial operator*( Variable& v, double c ); // product LinearPolynomial operator/( Variable& v, double c ); // quotient // algebraic operations between single variables and constants LinearPolynomial operator+( Variable& v1, Variable& v2 ); // sum LinearPolynomial operator-( Variable& v1, Variable& v2 ); // difference // algebraic operations between pairs of variables LinearPolynomial operator+( const LinearPolynomial& p, double c ); // sum LinearPolynomial operator+( double c, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, double c ); // difference LinearPolynomial operator-( double c, const LinearPolynomial& p ); // difference LinearPolynomial operator*( const LinearPolynomial& p, double c ); // product LinearPolynomial operator*( double c, const LinearPolynomial& p ); // product LinearPolynomial operator/( const LinearPolynomial& p, double c ); // quotient // algebraic operations between polynomials and constants LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ); // sum LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ); // sum LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ); // difference LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ); // difference // algebraic operations between polynomials and single variables LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ); // sum LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ); // difference // algebraic operations between pairs of polynomials std::ostream& operator<<( std::ostream& os, const LinearPolynomial& p ); // prints the symbolic representation of a polynomial (all variables must be named) } #endif ================================================ FILE: Hot2/include/LinearSystem.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- LinearSystem.h // ----------------------------------------------------------------------------- // // LinearSystem represents a system of linear equations expressed in terms of // instances of the Variable class. The main idea is to make it easy to // construct and solve linear systems without explicitly think about variable // indices, matrix layout, etc. (This kind of abstraction is particularly // useful for debugging and rapid prototyping.) See the documentation for // examples of building linear and solving systems. // // Importantly, any variable used by a LinearSystem should not be deallocated // while the system is still in use, because the method LinearSystem::solve() // automatically copies the solution back into the variables used to define the // equations. (In the future variables may become reference-counted in order // to avoid this issue.) // // Note that LinearSystem::solve() uses a general-purpose linear solver (namely // the sparse QR factorization found in SuiteSparse) that is quite fast but may // not always be your best option. To improve performance you may want to // build the system explicitly using an instance of SparseMatrix and call a // more specialized solver. (In the future there may be options for specifying // that a LinearSystem is, e.g., symmetric and positive-definite.) // #ifndef DDG_LINEARSYSTEM_H #define DDG_LINEARSYSTEM_H #include #include "LinearEquation.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "Real.h" namespace DDG { class LinearSystem { public: void clear( void ); // removes all equations from the system void push_back( const LinearEquation& e ); // appends the equation e to the sytem void solve( void ); // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution std::vector equations; // the collection of equations defining the system protected: void convertEquations( void ); void indexVariables( void ); void buildSparseMatrix( void ); void buildRightHandSide( void ); void computeSolution( void ); int nEquations; int nVariables; std::vector currentEquations; std::map index; SparseMatrix A; DenseMatrix x; DenseMatrix b; }; } #endif ================================================ FILE: Hot2/include/Mesh.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Mesh.h // ----------------------------------------------------------------------------- // // Mesh represents a polygonal surface mesh using the halfedge data structure. // It is essentially a large collection of disjoint vertices, edges, and faces // that are ``glued together'' by halfedges which encode connectivity (see // the documentation for an illustration). By construction, the halfedge data // structure cannot represent nonorientable surfaces or meshes with nonmanifold // edges. // // Mesh elements are referenced using iterators -- common usage of these // iterators is to either traverse an entire vector of mesh elements: // // // visit all vertices // for( VertexIter i = vertices.begin(); i != vertices.end(); i++ ) // { // //... // } // // or to perform a local traversal over the neighborhood of some mesh element: // // // visit both halfedges of edge e // HalfEdgeIter he = e->he; // do // { // // ... // // he = he->flip; // } // while( he != e->he ); // // (See Types.h for an explicit definition of iterator types.) // // Meshes with boundary are handled by creating an additional face for each // boundary loop (the method Face::isBoundary() determines whether a given // face is a boundary loop). Isolated vertices (i.e., vertiecs not contained // in any edge or face) reference a dummy halfedge and can be checked via // the method Vertex::isIsolated(). // #ifndef DDG_MESH_H #define DDG_MESH_H #include #include #include "HalfEdge.h" #include "Vertex.h" #include "Edge.h" #include "Face.h" #include "SparseMatrix.h" namespace DDG { class Mesh { public: Mesh( void ); // constructs an empty mesh Mesh( const Mesh& mesh ); // constructs a copy of mesh const Mesh& operator=( const Mesh& mesh ); // copies mesh int read( const std::string& filename ); // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error int write( const std::string& filename ) const; // writes a mesh to a Wavefront OBJ file; return value is nonzero // only if there was an error bool reload( void ); // reloads a mesh from disk using the most recent input filename void normalize( void ); // centers around the origin and rescales to have unit radius double area( void ) const; // returns total mesh area double meanEdgeLength( void ) const; // returns mean edge lenght std::vector halfedges; std::vector vertices; std::vector edges; std::vector faces; std::vector boundaries; // storage for mesh elements protected: std::string inputFilename; void indexElements( void ); // assigns a unique, 0-based index to each mesh element }; } #endif ================================================ FILE: Hot2/include/MeshIO.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- MeshIO.h // ----------------------------------------------------------------------------- // // MeshIO handles input/output operations for Mesh objects. Currently the only // supported mesh format is Wavefront OBJ -- for a format specification see // // http://en.wikipedia.org/wiki/Wavefront_.obj_file // // Note that vertex normals and material properties are currently ignored. // #ifndef DDG_MESHIO_H #define DDG_MESHIO_H #include #include #include #include namespace DDG { class Mesh; class Index; class MeshData; class MeshIO { public: static int read( std::istream& in, Mesh& mesh ); // reads a mesh from a valid, open input stream in static void write( std::ostream& out, const Mesh& mesh ); // writes a mesh to a valid, open output stream out protected: static int readMeshData( std::istream& in, MeshData& data ); static void readPosition( std::stringstream& ss, MeshData& data ); static void readTexCoord( std::stringstream& ss, MeshData& data ); static void readNormal ( std::stringstream& ss, MeshData& data ); static void readFace ( std::stringstream& ss, MeshData& data ); static Index parseFaceIndex( const std::string& token ); static void preallocateMeshElements( const MeshData& data, Mesh& mesh ); static int buildMesh( const MeshData& data, Mesh& mesh ); static void checkIsolatedVertices( const Mesh& Mesh ); static void checkNonManifoldVertices( const Mesh& Mesh ); }; } #endif ================================================ FILE: Hot2/include/Quaternion.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Quaternion.h // ----------------------------------------------------------------------------- // // Quaternion represents an element of the quaternions, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // Hamilton product is expressed using the * operator: // // Quaternion p, q, r; // r = q * p; // // and conjugation is expressed using the method Quaternion::conj(): // // Quaternion q; // double normQSquared = -q.conj()*q; // // Individual components can be accessed in several ways: the real and imaginary // parts can be accessed using the methods Quaternion::re() and Quaternion::im(): // // Quaternion q; // double a = q.re(); // Vector b = q.im(); // // or by index: // // Quaternion q; // double a = q[0]; // double bi = q[1]; // double bj = q[2]; // double bk = q[3]; // #ifndef DDG_QUATERNION_H #define DDG_QUATERNION_H #include "Vector.h" #include "Complex.h" #include namespace DDG { class Quaternion { public: Quaternion( void ); // initializes all components to zero Quaternion( const Quaternion& q ); // initializes from existing quaternion Quaternion( double s, double vi, double vj, double vk ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s, const Vector& v ); // initializes with specified real (s) and imaginary (v) components Quaternion( double s ); // initializes purely real quaternion with specified real (s) component (imaginary part is zero) Quaternion( const Vector& v ); // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) Quaternion( const Complex& z ); // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k const Quaternion& operator=( double s ); // assigns a purely real quaternion with real value s const Quaternion& operator=( const Vector& v ); // assigns a purely real quaternion with imaginary value v double& operator[]( int index ); // returns reference to the specified component (0-based indexing: r, i, j, k) const double& operator[]( int index ) const; // returns const reference to the specified component (0-based indexing: r, i, j, k) void toMatrix( double Q[4][4] ) const; // builds 4x4 matrix Q representing (left) quaternion multiplication double& re( void ); // returns reference to double part const double& re( void ) const; // returns const reference to double part Vector& im( void ); // returns reference to imaginary part const Vector& im( void ) const; // returns const reference to imaginary part Quaternion operator+( const Quaternion& q ) const; // addition Quaternion operator-( const Quaternion& q ) const; // subtraction Quaternion operator-( void ) const; // negation Quaternion operator*( double c ) const; // right scalar multiplication Quaternion operator/( double c ) const; // scalar division void operator+=( const Quaternion& q ); // addition / assignment void operator+=( double c ); // addition / assignment of pure real void operator-=( const Quaternion& q ); // subtraction / assignment void operator-=( double c ); // subtraction / assignment of pure real void operator*=( double c ); // scalar multiplication / assignment void operator/=( double c ); // scalar division / assignment Quaternion operator*( const Quaternion& q ) const; // Hamilton product void operator*=( const Quaternion& q ); // Hamilton product / assignment Quaternion conj( void ) const; // conjugation Quaternion inv( void ) const; // inverse double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Quaternion unit( void ) const; // returns unit quaternion void normalize( void ); // divides by Euclidean length protected: double s; // scalar (double) part Vector v; // vector (imaginary) part }; Quaternion operator*( double c, const Quaternion& q ); // left scalar multiplication std::ostream& operator<<( std::ostream& os, const Quaternion& q ); // prints components } #endif ================================================ FILE: Hot2/include/Real.h ================================================ #ifndef DDG_REAL_H #define DDG_REAL_H namespace DDG { class Real { public: Real( double x = 0. ); // constructs real number with value x operator double( void ) const; // type cast to double void operator+=( double x ); // increment void operator-=( double x ); // decrement void operator*=( double x ); // multiply void operator/=( double x ); // divide Real conj( void ) const; // simply returns the value (for compatibility w/ complex numbers) Real inv( void ) const; // returns inverse double norm( void ) const; // returns norm double norm2( void ) const; // returns norm squared Real unit( void ) const; // returns number with unit norm and same sign protected: double value; // value }; } #endif ================================================ FILE: Hot2/include/Shader.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Shader.h // ----------------------------------------------------------------------------- // // Shader encapsulates the functionality of a shader program written in // the OpenGL Shader Language (GLSL). Basic usage is to read a collection // of source files to disk and enable the shader before making draw calls. // For instance, during initialization one might write // // Shader shader; // shader.loadVertex( "vertex.glsl" ); // shader.loadFragment( "fragment.glsl" ); // // and in the main draw routine write // // shader.enable(); // // draw some stuff // shader.disable(); // #ifndef DDG_SHADER_H #define DDG_SHADER_H #include #include namespace DDG { class Shader { public: Shader( void ); // constructor -- shader is initially invalid ~Shader( void ); // destructor void loadVertex( const char* filename ); // read vertex shader from GLSL source file void loadFragment( const char* filename ); // read fragment shader from GLSL source file void loadGeometry( const char* filename ); // read geometry shader from GLSL source file void enable( void ); // uses this shader for rendering void disable( void ) const; // uses the fixed-function pipeline for rendering operator GLuint( void ) const; // returns the ID of this shader program (for calls to OpenGL) protected: void load( GLenum shaderType, const char* filename, GLuint& shader ); bool readSource( const char* filename, std::string& source ); GLuint vertexShader; GLuint fragmentShader; GLuint geometryShader; GLuint program; bool linked; }; } #endif ================================================ FILE: Hot2/include/SparseMatrix.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- SparseMatrix.h // ----------------------------------------------------------------------------- // // SparseMatrix represents an m by n (real or complex) matrix where only // nonzero entries are stored explicitly. This class is most commonly // used to represent the linear term in sparse linear systems (i.e., the matrix // part). // // A real or complex matrix is allocated via // // SparseMatrix A( m, n ); // SparseMatrix A( m, n, entryComplex ); // // Matrix elements are then accessed using parenthesis, e.g., // // A(i,j) = 1; // A(i,j) += 2; // a = A(i,j); // // etc. // // SparseMatrix is interoperable with the SuiteSparse numerical linear algebra // library. In particular, dereferencing a SparseMatrix returns a // cholmod_sparse* which can be used by routines in SuiteSparse. For basic // operations, however, you should not need to access this pointer explicitly -- // see the solve() method below. // // Internally SparseMatrix stores nonzero entries in a heap data structure; the // amortized cost of insertion is therefore no worse than the sorting cost of // putting the matrix in compressed-column order. // #ifndef DDG_SPARSE_MATRIX_H #define DDG_SPARSE_MATRIX_H #include #include #include #include "Types.h" namespace DDG { template class SparseMatrix { public: SparseMatrix( int m = 0, int n = 1 ); // initialize an mxn matrix SparseMatrix( const SparseMatrix& B ); // copy constructor ~SparseMatrix( void ); // destructor const SparseMatrix& operator=( const SparseMatrix& B ); // copies B const SparseMatrix& operator=( cholmod_sparse* B ); // copies a cholmod_sparse* into a SparseMatrix; // takes responsibility for deallocating B void resize( int m, int n ); // clears and resizes to mxn matrix SparseMatrix transpose( void ) const; // returns the transpose of this matrix cholmod_sparse* to_cholmod( void ); // returns pointer to copy of matrix in compressed-column CHOLMOD format SparseMatrix operator*( const SparseMatrix& B ) const; // returns product of this matrix with sparse B DenseMatrix operator*( const DenseMatrix& B ) const; // returns product of this matrix with dense B void operator*=( const T& c ); // multiplies this matrix by the scalar c void operator/=( const T& c ); // divides this matrix by the scalar c void operator+=( const SparseMatrix& B ); // adds B to this matrix void operator-=( const SparseMatrix& B ); // subtracts B from this matrix SparseMatrix operator+( const SparseMatrix& B ) const; // returns sum of this matrix with B SparseMatrix operator-( const SparseMatrix& B ) const; // returns difference of this matrix with B int nRows( void ) const; // returns the number of rows int nColumns( void ) const; // returns the number of columns int length( void ) const; // returns the size of the largest dimension void zero( const T& val ); // sets all nonzero elements val SparseMatrix inverse( void ) const; // returns inverse -- for diagonal matrices only // (assertion occurs for non-diagonal matrices) static SparseMatrix identity( int N ); // returns the N x N identity matrix DenseMatrix full( void ) const; // converts to a dense matrix T& operator()( int row, int col ); T operator()( int row, int col ) const; // access the specified element (uses 0-based indexing) // TODO for legibility, replace w/ type where entries are named "row, // TODO col" instead of "first, second" (especially since we adopt the // TODO unorthodox convention of storing the column first) typedef std::pair EntryIndex; // convenience type for an entry index; note that we store column THEN // row, which makes it easier to build compressed column format typedef std::map EntryMap; typedef typename EntryMap::iterator iterator; typedef typename EntryMap::const_iterator const_iterator; // convenience types for storing and accessing entries iterator begin( void ); const_iterator begin( void ) const; iterator end( void ); const_iterator end( void ) const; // return iterators to first and last nonzero entries void shift( double c ); // adds c times the identity matrix to this matrix protected: int m, n; EntryMap data; cholmod_sparse* cData; void allocateSparse( void ); void setEntry( const_iterator e, int i, double* pr ); }; template SparseMatrix operator*( const SparseMatrix& A, const T& c ); // right scalar multiplication template SparseMatrix operator*( const T& c, const SparseMatrix& A ); // left scalar multiplication template SparseMatrix operator/( const SparseMatrix& A, const T& c ); // scalar division template std::ostream& operator << (std::ostream& os, const SparseMatrix& o); // prints entries template class SparseFactor { public: SparseFactor( void ); ~SparseFactor( void ); void build( SparseMatrix& A ); // factorizes positive-definite matrix A using CHOLMOD bool valid( void ) const; // returns true if the factor has been built; false otherwise cholmod_factor* to_cholmod( void ); // returns pointer to underlying cholmod_factor data structure protected: cholmod_factor *L; }; template void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse QR factorization template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the sparse linear system Ax = b using sparse LU factorization template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ); // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ); // backsolves the prefactored positive definite sparse linear system LL'x = b template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector = true ); // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ); // solves A x = lambda B x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; x is used as an initial guess template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ); // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ); // returns the max residual of the linear problem A x = b relative to the largest entry of the solution template double residual( const SparseMatrix& A, const DenseMatrix& x ); // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda B x relative to the largest entry of the solution template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ); // returns / template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ); // returns /<(B-EE^T)x,x> } #include "SparseMatrix.inl" #endif ================================================ FILE: Hot2/include/Types.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Types.h // ----------------------------------------------------------------------------- // // This file contains forward declarations of common types and definitions of // convenience types for standard iterators. // #ifndef DDG_TYPES_H #define DDG_TYPES_H #include #include #include namespace DDG { // forward declarations class Camera; class Complex; class Edge; class Face; class HalfEdge; class Image; class LinearContext; class LinearEquation; class LinearPolynomial; class LinearSystem; class Mesh; class MeshIO; class Quaternion; class Real; class Shader; class Variable; class Vector; class Vertex; class Viewer; template class DenseMatrix; template class SparseMatrix; // convenience types for iterators typedef std::map::iterator TermIter; typedef std::map::const_iterator TermCIter; typedef std::vector::iterator PolyIter; typedef std::vector::const_iterator PolyCIter; typedef std::vector::iterator EqnIter; typedef std::vector::const_iterator EqnCIter; typedef std::map::iterator IndexIter; typedef std::map::const_iterator IndexCIter; typedef std::vector::iterator HalfEdgeIter; typedef std::vector::const_iterator HalfEdgeCIter; typedef std::vector::iterator VertexIter; typedef std::vector::const_iterator VertexCIter; typedef std::vector::iterator EdgeIter; typedef std::vector::const_iterator EdgeCIter; typedef std::vector::iterator FaceIter; typedef std::vector::const_iterator FaceCIter; } #endif ================================================ FILE: Hot2/include/Utility.h ================================================ #ifndef DDG_UTILITY_H #define DDG_UTILITY_H #include #include "Utility.h" #include "Complex.h" namespace DDG { inline double sqr( double x ) { return x*x; } inline double unitRand( void ) { const double rRandMax = 1. / (double) RAND_MAX; return rRandMax * (double) rand(); } inline double seconds( int t0, int t1 ) { return (double)(t1-t0) / (double) CLOCKS_PER_SEC; } } namespace DDGConstants { static DDG::Complex ii( 0., 1. ); } #endif ================================================ FILE: Hot2/include/Variable.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Variable.h // ----------------------------------------------------------------------------- // // Variable represents a variable that can be used to define a (linear or // nonlinear) system of equations. Its main feature is that it can be used // both as an abstract variable (e.g., when used to define an equation) but can // also store a definite numerical value. For instance, suppose we define a // linear polynomial // // Variable x, y; // LinearPolynomial p = x + 2*y; // // Now by assigning different numerical values to x and y, we can evaluate the // polynomial at different points: // // *x = 1; // *y = 2; // cout << p.evaluate() << endl; // // *x = 3; // *y = 4; // cout << p.evaluate() << endl; // // In general the dereference operator (*) accesses the numerical value. // Variables can also be named in order to aid with debugging. For instance, // // Variable x( "x" ); // Variable y( "y" ); // Polynomial p = x + 2*y; // cout << p << endl; // // will print out something like "x+2*y". // // The "fixed" flag in a variable refers to whether it is held constant while // solving a system of equations -- see the documentation for further discussion. // #ifndef DDG_VARIABLE_H #define DDG_VARIABLE_H #include namespace DDG { class Variable { public: Variable( double value = 0., bool fixed = false ); // initialize a variable which has value zero and is not fixed by default Variable( std::string name, double value = 0., bool fixed = false ); // initialize a named variable which has value zero and is not fixed by default double& operator*( void ); // returns a reference to the numerical value const double& operator*( void ) const; // returns a const reference to the numerical value std::string name; // names the variable (for display output) double value; // numerical value bool fixed; // true if a variable is held constant while solving a system of equations }; } #endif ================================================ FILE: Hot2/include/Vector.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vector.h // ----------------------------------------------------------------------------- // // Vector represents an element of Euclidean 3-space, along with all the usual // vectors space operations (addition, multiplication by scalars, etc.). The // inner product (i.e., scalar or dot product) is expressed using the global // method dot(): // // Vector u, v; // double cosTheta = dot( u, v ); // // and the cross product is expressed using the global method cross(): // // Vector u, v, w; // w = cross( u, v ); // // Individual components can be accessed in two ways: either directly via the // members x, y, and z: // // Vector v; // cout << v.x << endl; // cout << v.y << endl; // cout << v.z << endl; // // or by index: // // Vector v; // for( int i = 0; i < 3; i++ ) // { // cout << v[i] << endl; // } // #ifndef DDG_VECTOR_H #define DDG_VECTOR_H #include namespace DDG { class Vector { public: Vector(); // initializes all components to zero Vector( double x, double y, double z); // initializes with specified components Vector( const Vector& v ); // initializes from existing vector double& operator[] ( const int& index ); // returns reference to the specified component (0-based indexing: x, y, z) const double& operator[] ( const int& index ) const; // returns const reference to the specified component (0-based indexing: x, y, z) Vector operator+( const Vector& v ) const; // addition Vector operator-( const Vector& v ) const; // subtraction Vector operator-( void ) const; // negation Vector operator*( const double& c ) const; // right scalar multiplication Vector operator/( const double& c ) const; // scalar division void operator+=( const Vector& v ); // addition / assignment void operator-=( const Vector& v ); // subtraction / assignment void operator*=( const double& c ); // scalar multiplication / assignment void operator/=( const double& c ); // scalar division / assignment double norm( void ) const; // returns Euclidean length double norm2( void ) const; // returns Euclidean length squared Vector unit( void ) const; // returns unit vector void normalize( void ); // divides by Euclidean length Vector abs( void ) const; // returns vector containing magnitude of each component double x, y, z; // components }; Vector operator* ( const double& c, const Vector& v ); // left scalar multiplication double dot( const Vector& u, const Vector& v ); // dot product (a.k.a. inner or scalar product) Vector cross( const Vector& u, const Vector& v ); // cross product std::ostream& operator << (std::ostream& os, const Vector& o); // prints components } #endif ================================================ FILE: Hot2/include/Vertex.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Vertex.h // ----------------------------------------------------------------------------- // // Vertex stores attributes associated with a mesh edge. The iterator he // points to its "outgoing" halfedge. (See the documentation for a more // in-depth discussion of the halfedge data structure.) // #ifndef DDG_VERTEX_H #define DDG_VERTEX_H #include "Vector.h" #include "Types.h" namespace DDG { class Vertex { public: HalfEdgeIter he; // points to the "outgoing" halfedge Vector position; // location of vertex in Euclidean 3-space int index; // unique integer ID in the range 0, ..., nVertices-1 bool tag; // true if vertex is selected by the user; false otherwise double weight; // vertex weight to construct orthogonal dual mesh Vertex() : index(0), tag(false), weight(0.0) { } double area( void ) const; // returns the barycentric area associated with this vertex Vector normal( void ) const; // returns the vertex normal bool isIsolated( void ) const; // returns true if the vertex is not contained in any face or edge; false otherwise int valence( void ) const; // returns the number of incident faces / edges void toggleTag(); // toggle vertex tag }; } #endif ================================================ FILE: Hot2/include/Viewer.h ================================================ // ----------------------------------------------------------------------------- // libDDG -- Viewer.h // ----------------------------------------------------------------------------- // // Viewer provides a graphical user interface (GUI) for inspecting and // interacting with a Mesh object. Viewer methods are static in order // to make them compatible with GLUT callbacks. // #ifndef DDG_VIEWER_H #define DDG_VIEWER_H #include #include "Mesh.h" #include "Camera.h" #include "Shader.h" namespace DDG { class Viewer { public: static void init( void ); // displays the viewer until the program ends static Mesh mesh; // surface mesh visualized by Viewer protected: // init static void initGLUT( void ); static void initGLSL( void ); // GLUT callbacks static void display( void ); static void idle( void ); static void keyboard( unsigned char c, int x, int y ); static void special( int i, int x, int y ); static void mouse( int button, int state, int x, int y ); static void motion( int x, int y ); static void menu( int value ); static void view( int value ); // menu functions static void mProcess( void ); static void mResetMesh( void ); static void mWriteMesh( void ); static void mExit( void ); static void mWireframe( void ); static void mZoomIn( void ); static void mZoomOut( void ); static void mScreenshot( void ); static void mDualMesh( void ); static void mWeights( void ); // unique identifiers for menus enum { menuProcess, menuResetMesh, menuWriteMesh, menuExit, menuWireframe, menuZoomIn, menuZoomOut, menuScreenshot, menuDualMesh, menuWeights }; // draw routines static void setGL( void ); static void setLighting( void ); static void setMeshMaterial( void ); static void callDisplayList( void ); static void updateDisplayList( void ); static void drawScene( void ); static void drawPolygons( void ); static void drawWireframe( void ); static void drawVertices( void ); static void drawSelectedVertices( void ); static void drawIsolatedVertices( void ); static void pickVertex(int x, int y); static void drawDualMesh( void ); static void drawWeights( void ); static void storeViewerState( void ); static void restoreViewerState( void ); static int windowSize[2]; static bool renderWireframe; // draw wireframe static bool renderDualMesh; // darw dual mesh static bool renderWeights; // darw dual mesh static Camera camera; // keeps track of view state static GLuint surfaceDL; // display list for mesh static Shader shader; // shader used to determine appearance of surface }; } #endif ================================================ FILE: Hot2/obj/.empty ================================================ ================================================ FILE: Hot2/shaders/fragment.glsl ================================================ uniform vec3 eye; uniform vec3 light; varying vec3 position; varying vec3 normal; float diffuse( vec3 N, vec3 L ) { return max( 0., dot( N, L )); } float specular( vec3 N, vec3 L, vec3 E ) { const float shininess = 8.; vec3 R = 2.*dot(L,N)*N - L; return pow( max( 0., dot( R, E )), shininess ); } float fresnel( vec3 N, vec3 E ) { const float sharpness = 10.; float NE = max( 0., dot( N, E )); return pow( sqrt( 1. - NE*NE ), sharpness ); } void main() { vec3 N = normalize( normal ); vec3 L = normalize( light - position ); vec3 E = normalize( eye - position ); vec3 R = 2.*dot(L,N)*N - L; vec3 one = vec3( 1., 1., 1. ); gl_FragColor.rgb = diffuse(N,L)*gl_Color.rgb + .5*specular(N,L,E)*one + .5*fresnel(N,E)*one; gl_FragColor.a = 1.; } ================================================ FILE: Hot2/shaders/vertex.glsl ================================================ varying vec3 position; varying vec3 normal; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_FrontColor = gl_Color; position = gl_Vertex.xyz; normal = gl_Normal.xyz; } ================================================ FILE: Hot2/src/Camera.cpp ================================================ #include #include #include using namespace std; #include "Camera.h" namespace DDG { Camera :: Camera( void ) : pClick( 1. ), pDrag( 1. ), pLast( 1. ), rLast( 1. ), momentum( 1. ), zoom( 1. ) {} Quaternion Camera :: clickToSphere( int x, int y ) { GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); int w = viewport[2]; int h = viewport[3]; Quaternion p( 0., 2. * (double) x / (double) w - 1., 2. * (double) y / (double) h - 1., 0. ); if( p.norm2() > 1. ) { p.normalize(); p.im().z = 0.; } else { p.im().z = sqrt( 1. - p.norm2() ); } return p; } Quaternion Camera :: currentRotation( void ) const { return ( pDrag * pClick.conj() ) * rLast; } void Camera :: setView( void ) const { Quaternion r = ( pDrag * pClick.conj() ) * rLast; double w = r[0]; double x = r[1]; double y = r[2]; double z = r[3]; GLdouble M[16] = { 1.-2.*y*y-2.*z*z, 2.*x*y+2.*w*z, 2.*x*z-2.*w*y, 0., 2.*x*y-2.*w*z, 1.-2.*x*x-2.*z*z, 2.*y*z+2.*w*x, 0., 2.*x*z+2.*w*y, 2.*y*z-2.*w*x, 1.-2.*x*x-2.*y*y, 0., 0., 0., 0., 1. }; glMatrixMode( GL_MODELVIEW ); glMultMatrixd( M ); } void Camera :: mouse( int button, int state, int x, int y ) { if( state == GLUT_DOWN ) { pClick = pDrag = pLast = clickToSphere( x, y ); momentum = 1.; } if( state == GLUT_UP ) { double timeSinceDrag = ( clock() - tLast ) / (double) CLOCKS_PER_SEC; if( timeSinceDrag < .1 ) { momentum = pDrag * pLast.conj(); momentum = ( .03 * momentum + .97 ).unit(); } else { momentum = 1.; } rLast = pDrag * pClick.conj() * rLast; pClick = pDrag = 1.; } } void Camera :: motion( int x, int y ) { tLast = clock(); pLast = pDrag; pDrag = clickToSphere( x, y ); } void Camera :: idle( void ) { // get time since last idle event static int t0 = clock(); int t1 = clock(); double dt = (t1-t0) / (double) CLOCKS_PER_SEC; rLast = momentum * rLast; momentum = ( (1.-.5*dt) * momentum + .5*dt ).unit(); zoom += vZoom*dt; vZoom *= max( 0., 1.-5.*dt ); t0 = t1; } void Camera :: zoomIn( void ) { vZoom -= 0.5; } void Camera :: zoomOut( void ) { vZoom += 0.5; } } ================================================ FILE: Hot2/src/Complex.cpp ================================================ #include "Complex.h" #include #include using namespace std; namespace DDG { Complex::Complex( double a, double b ) // constructs number a+bi : re( a ), im( b ) {} void Complex::operator+=( const Complex& z ) // add z { re += z.re; im += z.im; } void Complex::operator-=( const Complex& z ) // subtract z { re -= z.re; im -= z.im; } void Complex::operator*=( const Complex& z ) // Complex multiply by z { double a = re; double b = im; double c = z.re; double d = z.im; re = a*c-b*d; im = a*d+b*c; } void Complex::operator*=( double r ) // scalar multiply by r { re *= r; im *= r; } void Complex::operator/=( double r ) // scalar divide by r { re /= r; im /= r; } void Complex::operator/=( const Complex& z ) // scalar divide by r { *this *= z.inv(); } Complex Complex::operator-( void ) const { return Complex( -re, -im ); } Complex Complex::conj( void ) const // returns Complex conjugate { return Complex( re, -im ); } Complex Complex::inv( void ) const // returns inverse { return this->conj() / this->norm2(); } double Complex::arg( void ) const // returns argument { return atan2( im, re ); } double Complex::norm( void ) const // returns norm { return sqrt( re*re + im*im ); } double Complex::norm2( void ) const // returns norm squared { return re*re + im*im; } Complex Complex::unit( void ) const // returns complex number with unit norm and same modulus { return *this / this->norm(); } Complex Complex::exponential( void ) const // complex exponentiation { return exp( re ) * Complex( cos( im ), sin( im )); } Complex operator+( const Complex& z1, const Complex& z2 ) // binary addition { Complex z = z1; z += z2; return z; } Complex operator-( const Complex& z1, const Complex& z2 ) // binary subtraction { Complex z = z1; z -= z2; return z; } Complex operator*( const Complex& z1, const Complex& z2 ) // binary Complex multiplication { Complex z = z1; z *= z2; return z; } Complex operator*( const Complex& z, double r ) // right scalar multiplication { Complex zr = z; zr *= r; return zr; } Complex operator*( double r, const Complex& z ) // left scalar multiplication { return z*r; } Complex operator/( const Complex& z, double r ) // scalar division { Complex zr = z; zr /= r; return zr; } Complex operator/( const Complex& z1, const Complex& z2 ) // complex division { Complex z = z1; z /= z2; return z; } double dot( const Complex& z1, const Complex& z2 ) { return z1.re*z2.re + z1.im*z2.im; } double cross( const Complex& z1, const Complex& z2 ) { return z1.re*z2.im - z1.im*z2.re; } std::ostream& operator<<( std::ostream& os, const Complex& z ) // prints components { if( z.im > 0 ) { os << z.re << " + " << z.im << "i"; } else if( z.im < 0 ) { os << z.re << " - " << -z.im << "i"; } else { os << z.re; } return os; } } ================================================ FILE: Hot2/src/DenseMatrix.cpp ================================================ #include "DenseMatrix.h" namespace DDG { template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i] = data[i]; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } int d = m; // leading dimension cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_COMPLEX, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { x[i*2+0] = data[i].re; x[i*2+1] = data[i].im; } return cData; } template <> cholmod_dense* DenseMatrix :: to_cholmod( void ) // returns pointer to underlying cholmod_dense data structure { assert( nColumns() == 1 ); if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } cData = cholmod_l_allocate_dense( m*4, 1, m*4, CHOLMOD_REAL, context ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { x[i*4+k] = data[i][k]; } } return cData; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = x[i]; } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m*n; i++ ) { data[i] = Complex( x[i*2+0], x[i*2+1] ); } return *this; } template <> const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) // copies a cholmod_dense* into a DenseMatrix; // takes responsibility for deallocating B { assert( B ); assert( B->xtype == CHOLMOD_REAL ); assert( B->ncol == 1 ); assert( B->nrow%4 == 0 ); if( cData ) { cholmod_l_free_dense( &cData, context ); } cData = B; m = cData->nrow/4; n = 1; data.resize( m*n ); double* x = (double*) cData->x; for( int i = 0; i < m; i++ ) { data[i] = Quaternion( x[i*4+0], x[i*4+1], x[i*4+2], x[i*4+3] ); } return *this; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 3; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { double x = o(i,j); if( x == 0. ) { os << " 0"; for( int k = 0; k < p+6; k++ ) { os << " "; } } else if( x > 0. ) { os << " " << x << " "; } else { os << x << " "; } } os << "]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "[ "; for( int j = 0; j < o.nColumns(); j++ ) { Complex z = o(i,j); if( z.re == 0. ) { os << " 0"; for( int k = 0; k < p+5; k++ ) { os << " "; } } else if( z.re > 0. ) { os << " " << z.re; } else { os << z.re; } if( z.im == 0 ) { os << " "; } else if( z.im >= 0 ) { os << "+"; } else { os << "-"; } if( z.im == 0. ) { for( int k = 0; k < p+8; k++ ) { os << " "; } } else { os << abs( z.im ) << "i "; } } os << " ]" << endl; } return os; } template <> std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) { const int p = 2; os.precision( p ); os << scientific; for( int i = 0; i < o.nRows(); i++ ) { os << "["; for( int j = 0; j < o.nColumns(); j++ ) { Quaternion q = o(i,j); os << " " << q; } os << " ]" << endl; } return os; } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i] = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { data[i].re = 2.*unitRand() - 1.; data[i].im = 2.*unitRand() - 1.; } } template <> void DenseMatrix :: randomize( void ) // replaces entries with uniformly distributed real random numbers in the interval [-1,1] { for( int i = 0; i < m*n; i++ ) { for( int k = 0; k < 4; k++ ) { data[i][k] = 2.*unitRand() - 1.; } } } } ================================================ FILE: Hot2/src/DenseMatrix.inl ================================================ #include #include #include #include using namespace std; #include "DenseMatrix.h" #include "LinearContext.h" #include "Quaternion.h" #include "SparseMatrix.h" #include "Utility.h" namespace DDG { extern LinearContext context; template DenseMatrix :: DenseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) { data.resize( m*n ); zero(); } template DenseMatrix :: DenseMatrix( const DenseMatrix& A ) // copy constructor : cData( NULL ) { *this = A; } template DenseMatrix :: ~DenseMatrix( void ) // destructor { if( cData != NULL ) { cholmod_l_free_dense( &cData, context ); } } template DenseMatrix DenseMatrix :: transpose( void ) const { const DenseMatrix& A( *this ); DenseMatrix AT( n, m ); for( int i = 0; i < n; i++ ) for( int j = 0; j < m; j++ ) { AT(i,j) = A(j,i).conj(); } return AT; } template SparseMatrix DenseMatrix::sparse( void ) // converts to a sparse matrix { SparseMatrix B; B = cholmod_l_dense_to_sparse( this->to_cholmod(), true, context ); return B; } template DenseMatrix DenseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); DenseMatrix AB( A.nRows(), B.nColumns() ); for( int i = 0; i < A.nRows(); i++ ) for( int j = 0; j < B.nColumns(); j++ ) for( int k = 0; k < A.nColumns(); k++ ) { AB( i, j ) += A( i, k ) * B( k, j ); } return AB; } template void DenseMatrix :: operator*=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c; } } template void DenseMatrix :: operator/=( const T& c ) { DenseMatrix& A( *this ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { A(i,j) *= c.inv(); } } template DenseMatrix DenseMatrix :: operator+( const DenseMatrix& B ) const // returns sum of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) + B(i,j); } return C; } template void DenseMatrix :: operator+=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) += B(i,j); } } template DenseMatrix DenseMatrix :: operator-( const DenseMatrix& B ) const // returns difference of this matrix with B { const DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); DenseMatrix C( nRows(), nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { C(i,j) = A(i,j) - B(i,j); } return C; } template void DenseMatrix :: operator-=( const DenseMatrix& B ) { DenseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( int i = 0; i < nRows(); i++ ) for( int j = 0; j < nColumns(); j++ ) { A(i,j) -= B(i,j); } } template DenseMatrix operator*( const T& c, const DenseMatrix& A ) { DenseMatrix cA = A; cA *= c; return cA; } template DenseMatrix operator*( const DenseMatrix& A, double c ) { return c*A; } template DenseMatrix operator/( const DenseMatrix& A, double c ) { DenseMatrix Ac = A; Ac /= c; return Ac; } template const DenseMatrix& DenseMatrix :: operator=( const DenseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_dense( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template int DenseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int DenseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int DenseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void DenseMatrix :: zero( const T& val ) // sets all elements to val { for( int i = 0; i < m*n; i++ ) { data[i] = val; } } template double DenseMatrix :: norm( NormType type ) const { double r = 0.; if( type == lInfinity ) { for( int i = 0; i < m*n; i++ ) { r = max( r, data[i].norm() ); } } else if( type == lOne ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm(); } } else if( type == lTwo ) { for( int i = 0; i < m*n; i++ ) { r += data[i].norm2(); } r = sqrt( r ); } return r; } template void DenseMatrix :: normalize( void ) // divides by l2 norm { *this /= norm( lTwo ); } template T& DenseMatrix :: operator()( int row, int col ) { return data[row+m*col]; } template T DenseMatrix :: operator()( int row, int col ) const { return data[row+m*col]; } template T& DenseMatrix :: operator()( int index ) { return data[index]; } template T DenseMatrix :: operator()( int index ) const { return data[index]; } template T DenseMatrix::sum( void ) const // returns the sum of all entries { T total( 0., 0. ); for( int i = 0; i < m*n; i++ ) { total += data[i]; } return total; } template void DenseMatrix :: removeMean( void ) { T mean = 0.; int N = m*n; for( int i = 0; i < N; i++ ) { mean += data[i]; } mean /= (double) N; for( int i = 0; i < N; i++ ) { data[i] -= mean; } } template T dot( const DenseMatrix& x, const DenseMatrix& y ) // returns Euclidean inner product of x and y { return ( x.transpose() * y )(0); } template DenseMatrix DenseMatrix::operator-( void ) const // returns additive inverse of this matrix { const DenseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < n; j++ ) { B( i, j ) = -A( i, j ); } return B; } template T inner( const DenseMatrix& x, const DenseMatrix& y ) // standard inner product { T sum = 0.; assert( x.nRows() == y.nRows() && x.nColumns() == y.nColumns() ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * y(i); } return sum; } template T inner( const DenseMatrix& x, const DenseMatrix& B, const DenseMatrix& y ) // inner product with respect a diagonal inner // product B represented as a dense vector { T sum = 0.; assert( x.nRows() == y.nRows() && x.nRows() == B.nRows() && x.nColumns() == 1 && B.nColumns() == 1 && y.nColumns() == 1 ); for( int i = 0; i < x.nRows()*x.nColumns(); i++ ) { sum += x(i).conj() * B(i) * y(i); } return sum; } } ================================================ FILE: Hot2/src/DiscreteExteriorCalculus.inl ================================================ #include "DiscreteExteriorCalculus.h" namespace DDG { template void HodgeStar0Form :: build( const Mesh& mesh, SparseMatrix& star0 ) // builds a diagonal matrix mapping primal discrete 0-forms // to dual discrete 2-forms { int nV = mesh.vertices.size(); star0 = SparseMatrix( nV, nV ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { int i = v->index; star0( i, i ) = v->area(); } } template void HodgeStar1Form :: build( const Mesh& mesh, SparseMatrix& star1 ) // builds a diagonal matrix mapping primal discrete 1-forms // to dual discrete 1-forms { int nE = mesh.edges.size(); star1 = SparseMatrix( nE, nE ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // get the cotangents of the two angles opposite this edge double cotAlpha = e->he->cotan(); double cotBeta = e->he->flip->cotan(); int i = e->index; star1( i, i ) = ( cotAlpha + cotBeta ) / 2.; } } template void HodgeStar2Form :: build( const Mesh& mesh, SparseMatrix& star2 ) // builds a diagonal matrix mapping primal discrete 2-forms // to dual discrete 2-forms { int nF = mesh.faces.size(); star2 = SparseMatrix( nF, nF ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { int i = f->index; star2( i, i ) = 1. / f->area(); } } template< class T > void ExteriorDerivative0Form :: build( const Mesh& mesh, SparseMatrix& d0 ) { int nV = mesh.vertices.size(); int nE = mesh.edges.size(); d0 = SparseMatrix( nE, nV ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { // the row index is the index of the edge int r = e->index; // the column indices are the indices of the two // edge vertices -- orientation is determined by // the orientation of the edge's first half edge int ci = e->he->vertex->index; int cj = e->he->flip->vertex->index; d0( r, ci ) = -1.; d0( r, cj ) = 1.; } } template< class T > void ExteriorDerivative1Form :: build( const Mesh& mesh, SparseMatrix& d1 ) { int nE = mesh.edges.size(); int nF = mesh.faces.size(); d1 = SparseMatrix( nF, nE ); // visit each face for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { // the row index is the index of the face int r = f->index; // visit all edges of this face HalfEdgeCIter he = f->he; do { // the column index is the index of the current edge int c = he->edge->index; // relative orientation is determined by checking if // the current half edge is the first half edge of its // corresponding edge double s = ( he->edge->he == he ? 1. : -1. ); // set the entry for this edge d1( r, c ) = s; he = he->next; } while( he != f->he ); } } } ================================================ FILE: Hot2/src/Edge.cpp ================================================ #include "Edge.h" #include "Mesh.h" namespace DDG { Vector Edge :: dualPoint() const { Vector pj = he->flip->vertex->position; Vector pi = he->vertex->position; Vector eij = (pj - pi).unit(); double dij = he->shift(); return pi + dij*eij; } } ================================================ FILE: Hot2/src/Face.cpp ================================================ #include "Face.h" #include "Mesh.h" #include "Vector.h" namespace DDG { double Face::area( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).norm() / 2.; } Vector Face::normal( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return cross( p1-p0, p2-p0 ).unit(); } bool Face::isBoundary( void ) const { return he->onBoundary; } Vector Face :: circumcenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector n = he->rotatedEdge(); double h = 0.5*he->cotan(); return 0.5*(p0+p1) + h*n; } Vector Face :: barycenter( void ) const { Vector p0 = he->vertex->position; Vector p1 = he->next->vertex->position; Vector p2 = he->next->next->vertex->position; return (p0 + p1 + p2)/3.; } Vector Face :: dualPoint( void ) const { Vector n = he->rotatedEdge().unit(); Vector c = he->edge->dualPoint(); double h = he->height(); return c + h*n; } } ================================================ FILE: Hot2/src/HalfEdge.cpp ================================================ #include "HalfEdge.h" #include "Mesh.h" namespace DDG { double HalfEdge :: cotan( void ) const { if( onBoundary ) return 0.0; Vector p0 = next->next->vertex->position; Vector p1 = vertex->position; Vector p2 = next->vertex->position; Vector u = p1-p0; Vector v = p2-p0; return dot( u, v ) / cross( u, v ).norm(); } Vector HalfEdge :: rotatedEdge( void ) const { if( onBoundary ) return Vector(); Vector n = face->normal(); Vector p0 = vertex->position; Vector p1 = flip->vertex->position; return cross( n, p1-p0 ); } double HalfEdge :: height( void ) const { if( onBoundary ) return 0.0; double cotk = cotan(); double coti = next->cotan(); double cotj = next->next->cotan(); double wk = next->next->vertex->weight; double wi = vertex->weight; double wj = next->vertex->weight; Vector pi = vertex->position; Vector pj = next->vertex->position; double lij = (pj - pi).norm(); return 0.5*(lij*cotk + (wi-wk)*cotj/lij + (wj-wk)*coti/lij); } double HalfEdge :: shift( void ) const { double wi = vertex->weight; double wj = flip->vertex->weight; Vector pi = vertex->position; Vector pj = flip->vertex->position; double lij = (pj - pi).norm(); return 0.5*(lij + (wi-wj)/lij); } } ================================================ FILE: Hot2/src/Image.cpp ================================================ #include #include #include #include using namespace std; #include "Image.h" namespace DDG { Image :: Image( int width, int height ) : w( width ), h( height ), pixels( w*h*3 ) {} float& Image :: operator()( int x, int y ) // accesses pixel (x,y) { return pixels[ x + y*w ]; } const float& Image :: operator()( int x, int y ) const // accesses pixel (x,y) { return pixels[ x + y*w ]; } float Image :: sample( float x, float y ) const // samples image at (x,y) using bilinear filtering { const Image& I( *this ); float ax = x - floor( x ); float ay = y - floor( y ); float bx = 1. - ax; float by = 1. - ay; int x0 = (int) floor( x ); int y0 = (int) floor( y ); int x1 = x0 + 1; int y1 = y0 + 1; clamp( x0, y0 ); clamp( x1, y1 ); return by * ( bx * I(x0,y0) + ax * I(x1,y0) ) + ay * ( bx * I(x0,y1) + ax * I(x1,y1) ) ; } int Image :: width( void ) const // returns image width { return w; } int Image :: height( void ) const // returns image height { return h; } class TGAHeader // header format for Truevision TGA images { public: char idFieldSize; char colorMapType; char dataTypeCode; short colorMapOrigin; short colorMapLength; char colorMapEntrySize; short xOrigin; short yOrigin; short width; short height; char bitsPerPixel; char imageSpecification; }; void Image :: read( const char* filename ) // loads an image file in Truevision TGA format // (must be uncompressed RGB image with 24 or 32 bits per pixel) { ifstream in( filename, ios_base::binary ); if( !in.is_open() ) { cerr << "Error: could not open file " << filename << " for input!" << endl; exit( 1 ); } // read header TGAHeader header; in.read( (char*) &(header.idFieldSize), 1 ); in.read( (char*) &(header.colorMapType), 1 ); in.read( (char*) &(header.dataTypeCode), 1 ); in.read( (char*) &(header.colorMapOrigin), 2 ); in.read( (char*) &(header.colorMapLength), 2 ); in.read( (char*) &(header.colorMapEntrySize), 1 ); in.read( (char*) &(header.xOrigin), 2 ); in.read( (char*) &(header.yOrigin), 2 ); in.read( (char*) &(header.width), 2 ); in.read( (char*) &(header.height), 2 ); in.read( (char*) &(header.bitsPerPixel), 1 ); in.read( (char*) &(header.imageSpecification), 1 ); w = header.width; h = header.height; // validate data type const char uncompressedRGB = 2; if( header.dataTypeCode != uncompressedRGB || ( header.bitsPerPixel != 24 && header.bitsPerPixel != 32 )) { cerr << "Error: input must be uncompressed RGB image with 24 or 32 bits per pixel." << endl; exit( 1 ); } // read identification field (unused) vector idField( header.idFieldSize ); in.read( &idField[0], header.idFieldSize ); // read color map data (unused) if( header.colorMapType == 1 ) { int bytesPerEntry = header.colorMapEntrySize / 8; int colorMapSize = header.colorMapLength * bytesPerEntry; vector colorMapData( colorMapSize ); in.read( &colorMapData[0], colorMapSize ); } // read pixel data int n = w*h*header.bitsPerPixel/8; vector pixelData( n ); in.read( (char*) &pixelData[0], n ); // convert pixel data to floating point pixels.resize( n ); for( int i = 0; i < n; i++ ) { pixels[i] = (double) pixelData[i] / 255.; } } void Image :: write( const char* filename ) const // writes an image file in Truevision TGA format // (uncompressed RGB image with 24 bits per pixel) { ofstream out( filename, ios_base::binary ); if( !out.is_open() ) { cerr << "Error: could not open file " << filename << " for output!" << endl; exit( 1 ); } TGAHeader header; header.idFieldSize = 0; header.colorMapType = 0; header.dataTypeCode = 2; header.colorMapOrigin = 0; header.colorMapLength = 0; header.colorMapEntrySize = 0; header.xOrigin = 0; header.yOrigin = 0; header.width = w; header.height = h; header.bitsPerPixel = 24; header.imageSpecification = 0; // write header out.write( (char*) &(header.idFieldSize), 1 ); out.write( (char*) &(header.colorMapType), 1 ); out.write( (char*) &(header.dataTypeCode), 1 ); out.write( (char*) &(header.colorMapOrigin), 2 ); out.write( (char*) &(header.colorMapLength), 2 ); out.write( (char*) &(header.colorMapEntrySize), 1 ); out.write( (char*) &(header.xOrigin), 2 ); out.write( (char*) &(header.yOrigin), 2 ); out.write( (char*) &(header.width), 2 ); out.write( (char*) &(header.height), 2 ); out.write( (char*) &(header.bitsPerPixel), 1 ); out.write( (char*) &(header.imageSpecification), 1 ); // convert pixel data from floating point vector pixelData( w*h*3 ); for( int i = 0; i < w*h*3; i++ ) { pixelData[i] = (unsigned char)( pixels[i] * 255. ); } // write pixel data out.write( (char*) &pixelData[0], w*h*3 ); } void Image :: clamp( int& x, int& y ) const // clamps coordinates to range [0,w-1] x [0,h-1] { x = max( 0, min( w-1, x )); y = max( 0, min( h-1, y )); } } ================================================ FILE: Hot2/src/LinearContext.cpp ================================================ #include "LinearContext.h" namespace DDG { // global context for linear solvers LinearContext context; LinearContext :: LinearContext( void ) // constructor { cholmod_l_start( &context ); } LinearContext :: ~LinearContext( void ) // destructor { cholmod_l_finish( &context ); } LinearContext :: operator cholmod_common*( void ) // allows LinearContext to be treated as a cholmod_common* { return &context; } } ================================================ FILE: Hot2/src/LinearEquation.cpp ================================================ #include "LinearEquation.h" namespace DDG { LinearEquation operator==( const LinearPolynomial& lhs, const LinearPolynomial& rhs ) // constructs a linear equation with the specified left- and right-hand side { LinearEquation eqn; eqn.lhs = lhs; eqn.rhs = rhs; return eqn; } } ================================================ FILE: Hot2/src/LinearPolynomial.cpp ================================================ #include using namespace std; #include "LinearPolynomial.h" #include "Types.h" namespace DDG { LinearPolynomial :: LinearPolynomial( void ) : constantTerm( 0. ) {} LinearPolynomial :: LinearPolynomial( double c ) { *this = c; } LinearPolynomial :: LinearPolynomial( Variable& v ) { *this = v; } const LinearPolynomial& LinearPolynomial :: operator=( double c ) { linearTerms.clear(); constantTerm = c; return *this; } const LinearPolynomial& LinearPolynomial :: operator=( Variable& v ) { linearTerms.clear(); linearTerms[ &v ] = 1.; constantTerm = 0.; return *this; } void LinearPolynomial::operator+=( double c ) { constantTerm += c; } void LinearPolynomial::operator-=( double c ) { constantTerm -= c; } void LinearPolynomial::operator*=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second *= c; } constantTerm *= c; } void LinearPolynomial::operator/=( double c ) { for( TermIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { i->second /= c; } constantTerm /= c; } void LinearPolynomial::operator+=( Variable& v ) { LinearPolynomial p( v ); *this += p; } void LinearPolynomial::operator-=( Variable& v ) { LinearPolynomial p( v ); *this -= p; } void LinearPolynomial::operator+=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second += i->second; } else { linearTerms[i->first] = i->second; } } constantTerm += e.constantTerm; } void LinearPolynomial::operator-=( const LinearPolynomial& e ) { for( TermCIter i = e.linearTerms.begin(); i != e.linearTerms.end(); i++ ) { TermIter j = linearTerms.find( i->first ); if( j != linearTerms.end() ) { j->second -= i->second; } else { linearTerms[i->first] = -i->second; } } constantTerm -= e.constantTerm; } LinearPolynomial LinearPolynomial::operator-( void ) const { LinearPolynomial p = *this; for( TermIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { i->second = -i->second; } p.constantTerm = -p.constantTerm; return p; } double LinearPolynomial::evaluate( void ) const { double value = constantTerm; for( TermCIter i = linearTerms.begin(); i != linearTerms.end(); i ++ ) { value += i->second * i->first->value; } return value; } LinearPolynomial operator+( double c, Variable& v ) { return c + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, double c ) { return LinearPolynomial(v) + c; } LinearPolynomial operator-( double c, Variable& v ) { return c - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, double c ) { return LinearPolynomial(v) - c; } LinearPolynomial operator*( double c, Variable& v ) { return LinearPolynomial(v) * c; } LinearPolynomial operator*( Variable& v, double c ) { return LinearPolynomial(v) * c; } LinearPolynomial operator/( Variable& v, double c ) { return LinearPolynomial(v) / c; } LinearPolynomial operator+( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) + LinearPolynomial(v2); } LinearPolynomial operator-( Variable& v1, Variable& v2 ) { return LinearPolynomial(v1) - LinearPolynomial(v2); } LinearPolynomial operator+( const LinearPolynomial& p, double c ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator+( double c, const LinearPolynomial& p ) { LinearPolynomial sum = p; sum += c; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, double c ) { LinearPolynomial difference = p; difference -= c; return difference; } LinearPolynomial operator-( double c, const LinearPolynomial& p ) { LinearPolynomial difference = -p; difference += c; return difference; } LinearPolynomial operator*( const LinearPolynomial& p, double c ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator*( double c, const LinearPolynomial& p ) { LinearPolynomial product = p; product *= c; return product; } LinearPolynomial operator/( const LinearPolynomial& p, double c ) { LinearPolynomial quotient = p; quotient /= c; return quotient; } LinearPolynomial operator+( const LinearPolynomial& p, Variable& v ) { return p + LinearPolynomial(v); } LinearPolynomial operator+( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) + p; } LinearPolynomial operator-( const LinearPolynomial& p, Variable& v ) { return p - LinearPolynomial(v); } LinearPolynomial operator-( Variable& v, const LinearPolynomial& p ) { return LinearPolynomial(v) - p; } LinearPolynomial operator+( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial sum = p; sum += q; return sum; } LinearPolynomial operator-( const LinearPolynomial& p, const LinearPolynomial& q ) { LinearPolynomial difference = p; difference -= q; return difference; } ostream& operator<<( ostream& os, const LinearPolynomial& p ) { for( TermCIter i = p.linearTerms.begin(); i != p.linearTerms.end(); i ++ ) { os << i->second << "*" << i->first->name << " + "; } os << p.constantTerm; return os; } } ================================================ FILE: Hot2/src/LinearSystem.cpp ================================================ #include using namespace std; #include #include "LinearSystem.h" #include "LinearContext.h" #include "Types.h" namespace DDG { extern LinearContext context; void LinearSystem::clear( void ) // removes all equations from the system { equations.clear(); } void LinearSystem::push_back( const LinearEquation& e ) // appends the equation e to the sytem { equations.push_back( e ); } void LinearSystem::solve( void ) // solves the system and automatically stores the result in the variables // for an overdetermined system, computes a least-squares solution { convertEquations(); indexVariables(); buildSparseMatrix(); buildRightHandSide(); computeSolution(); } void LinearSystem::convertEquations( void ) // converts each equation to its polynomial representation { currentEquations.clear(); for( EqnIter eqn = equations.begin(); eqn != equations.end(); eqn ++ ) { // move right-hand side to left-hand side LinearPolynomial p = eqn->lhs - eqn->rhs; // convert fixed variables to constants LinearPolynomial q( p.constantTerm ); for( TermIter t = p.linearTerms.begin(); t != p.linearTerms.end(); t ++ ) { const double& coefficient( t->second ); Variable& variable( *(t->first) ); // skip zeros if( coefficient == 0. ) continue; if( t->first->fixed ) { q += coefficient * variable.value; } else { q += coefficient * variable; } } if( q.linearTerms.size() > 0 ) { currentEquations.push_back( q ); } } nEquations = currentEquations.size(); } void LinearSystem::indexVariables( void ) // assign a unique index to each variable remaining in one of the polynomials { index.clear(); nVariables = 0; for( PolyCIter p = currentEquations.begin(); p != currentEquations.end(); p ++ ) { for( TermCIter t = p->linearTerms.begin(); t != p->linearTerms.end(); t ++ ) { IndexIter j = index.find( t->first ); // if we haven't seen this variable // before, assign it a unique index if( j == index.end() ) { index[ t->first ] = nVariables; nVariables++; } } } } void LinearSystem::buildSparseMatrix( void ) // build the sparse matrix representation of our current system { A = SparseMatrix( nEquations, nVariables ); for( int i = 0; i < nEquations; i++ ) { for( TermCIter t = currentEquations[i].linearTerms.begin(); t != currentEquations[i].linearTerms.end(); t ++ ) { int j = index[ t->first ]; A(i,j) = t->second; } } } void LinearSystem::buildRightHandSide( void ) // build the data vector for our current system { b = DenseMatrix( nEquations, 1 ); for( int i = 0; i < nEquations; i++ ) { b(i) = -currentEquations[i].constantTerm; } } void LinearSystem::computeSolution( void ) { // solve linear system Ax=b x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); // put solution values in variables for( IndexIter i = index.begin(); i != index.end(); i ++ ) { i->first->value = x( i->second ); } } } ================================================ FILE: Hot2/src/Mesh.cpp ================================================ #include #include #include "Mesh.h" #include "MeshIO.h" #include "DiscreteExteriorCalculus.h" using namespace std; namespace DDG { Mesh :: Mesh( void ) {} Mesh :: Mesh( const Mesh& mesh ) { *this = mesh; } class HalfEdgeIterCompare { public: bool operator()( const HalfEdgeIter& i, const HalfEdgeIter& j ) const { return &*i < &*j; } }; class HalfEdgeCIterCompare { public: bool operator()( const HalfEdgeCIter& i, const HalfEdgeCIter& j ) const { return &*i < &*j; } }; class VertexIterCompare { public: bool operator()( const VertexIter& i, const VertexIter& j ) const { return &*i < &*j; } }; class VertexCIterCompare { public: bool operator()( const VertexCIter& i, const VertexCIter& j ) const { return &*i < &*j; } }; class FaceIterCompare { public: bool operator()( const FaceIter& i, const FaceIter& j ) const { return &*i < &*j; } }; class FaceCIterCompare { public: bool operator()( const FaceCIter& i, const FaceCIter& j ) const { return &*i < &*j; } }; class EdgeIterCompare { public: bool operator()( const EdgeIter& i, const EdgeIter& j ) const { return &*i < &*j; } }; class EdgeCIterCompare { public: bool operator()( const EdgeCIter& i, const EdgeCIter& j ) const { return &*i < &*j; } }; const Mesh& Mesh :: operator=( const Mesh& mesh ) { map< HalfEdgeCIter, HalfEdgeIter, HalfEdgeCIterCompare > halfedgeOldToNew; map< VertexCIter, VertexIter, VertexCIterCompare > vertexOldToNew; map< EdgeCIter, EdgeIter, EdgeCIterCompare > edgeOldToNew; map< FaceCIter, FaceIter, FaceCIterCompare > faceOldToNew; // copy geometry from the original mesh and create a // map from pointers in the original mesh to // those in the new mesh halfedges.clear(); for( HalfEdgeCIter he = mesh.halfedges.begin(); he != mesh.halfedges.end(); he++ ) halfedgeOldToNew[ he ] = halfedges.insert( halfedges.end(), *he ); vertices.clear(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) vertexOldToNew[ v ] = vertices.insert( vertices.end(), *v ); edges.clear(); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e++ ) edgeOldToNew[ e ] = edges.insert( edges.end(), *e ); faces.clear(); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) faceOldToNew[ f ] = faces.insert( faces.end(), *f ); // "search and replace" old pointers with new ones for( HalfEdgeIter he = halfedges.begin(); he != halfedges.end(); he++ ) { he->next = halfedgeOldToNew[ he->next ]; he->flip = halfedgeOldToNew[ he->flip ]; he->vertex = vertexOldToNew[ he->vertex ]; he->edge = edgeOldToNew[ he->edge ]; he->face = faceOldToNew[ he->face ]; } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) v->he = halfedgeOldToNew[ v->he ]; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) e->he = halfedgeOldToNew[ e->he ]; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) f->he = halfedgeOldToNew[ f->he ]; return *this; } int Mesh::read( const string& filename ) { inputFilename = filename; ifstream in( filename.c_str() ); if( !in.is_open() ) { cerr << "Error reading from mesh file " << filename << endl; return 1; } int rval; if( !( rval = MeshIO::read( in, *this ))) { indexElements(); normalize(); } return rval; } int Mesh::write( const string& filename ) const // reads a mesh from a Wavefront OBJ file; return value is nonzero // only if there was an error { ofstream out( filename.c_str() ); if( !out.is_open() ) { cerr << "Error writing to mesh file " << filename << endl; return 1; } MeshIO::write( out, *this ); return 0; } bool Mesh::reload( void ) { return read( inputFilename ); } void Mesh::normalize( void ) { // compute center of mass Vector c( 0., 0., 0. ); for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { c += v->position; } c /= (double) vertices.size(); // translate to origin for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position -= c; } // rescale such that the mesh sits inside the unit ball double rMax = 0.; for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) { rMax = max( rMax, v->position.norm() ); } for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->position /= rMax; } } void Mesh::indexElements( void ) { int nV = 0; for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) { v->index = nV; nV++; } int nE = 0; for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) { e->index = nE; nE++; } int nF = 0; for( FaceIter f = faces.begin(); f != faces.end(); f++ ) { f->index = nF; nF++; } } double Mesh::area( void ) const { double sum = 0.0; for( FaceCIter f = faces.begin(); f != faces.end(); f ++ ) { sum += f->area(); } return sum; } double Mesh::meanEdgeLength( void ) const { double sum = 0; for( EdgeCIter e = edges.begin(); e != edges.end(); e ++) { VertexIter v0 = e->he->vertex; VertexIter v1 = e->he->flip->vertex; sum += (v0->position - v1->position).norm(); } return sum / edges.size(); } } ================================================ FILE: Hot2/src/MeshIO.cpp ================================================ #include #include #include #include #include "MeshIO.h" #include "Mesh.h" using namespace std; namespace DDG { class Index { public: Index( void ) {} Index( int p, int t, int n ) : position( p ), texcoord( t ), normal( n ) {} bool operator<( const Index& i ) const { if( position < i.position ) return true; if( position > i.position ) return false; if( texcoord < i.texcoord ) return true; if( texcoord > i.texcoord ) return false; if( normal < i.normal ) return true; if( normal > i.normal ) return false; return false; } int position; int texcoord; int normal; }; class MeshData { public: std::vector positions; std::vector texcoords; std::vector normals; std::vector< std::vector< Index > > indices; }; int MeshIO :: read( istream& in, Mesh& mesh ) // reads a mesh from a valid, open input stream in { MeshData data; if( readMeshData( in, data )) { return 1; } if( buildMesh( data, mesh )) { return 1; } return 0; } void MeshIO :: write( ostream& out, const Mesh& mesh ) // writes a mesh to a valid, open output stream out { int currentIndex = 1; map vertexIndex; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) { out << "v " << v->position.x << " " << v->position.y << " " << v->position.z << endl; vertexIndex[ v ] = currentIndex; currentIndex++; } for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeIter he = f->he; for( int j = 0; j < 3; j++ ) { out << "vt " << he->texcoord.x << " " << he->texcoord.y << endl; he = he->next; } } for( size_t i = 0; i < mesh.faces.size(); i++ ) { const Face& f( mesh.faces[i] ); HalfEdgeIter he = f.he; out << "f "; int j = 0; do { out << vertexIndex[ he->vertex ] << "/" << 1+(i*3+j) << " "; he = he->next; j++; } while( he != f.he ); out << endl; } } int MeshIO :: readMeshData( istream& in, MeshData& data ) { string line; while( getline( in, line )) { stringstream ss( line ); string token; ss >> token; if( token == "v" ) { readPosition( ss, data ); continue; } // vertex if( token == "vt" ) { readTexCoord( ss, data ); continue; } // texture coordinate if( token == "vn" ) { readNormal ( ss, data ); continue; } // vertex normal if( token == "f" ) { readFace ( ss, data ); continue; } // face if( token[0] == '#' ) continue; // comment if( token == "o" ) continue; // object name if( token == "g" ) continue; // group name if( token == "s" ) continue; // smoothing group if( token == "mtllib" ) continue; // material library if( token == "usemtl" ) continue; // material if( token == "" ) continue; // empty string cerr << "Error: does not appear to be a valid Wavefront OBJ file!" << endl; cerr << "(Offending line: " << line << ")" << endl; return 1; } return 0; } void MeshIO :: preallocateMeshElements( const MeshData& data, Mesh& mesh ) { // count the number of edges set< pair > edges; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { for( unsigned int I = 0; I < f->size(); I++ ) { int J = (I+1) % f->size(); int i = (*f)[I].position; int j = (*f)[J].position; if( i > j ) swap( i, j ); edges.insert( pair( i, j )); } } int nV = data.positions.size(); int nE = edges.size(); int nF = data.indices.size(); int nHE = 2*nE; int chi = nV - nE + nF; int nB = max( 0, 2 - chi ); // (conservative approximation of number of boundary cycles) mesh.halfedges.clear(); mesh.vertices.clear(); mesh.edges.clear(); mesh.faces.clear(); mesh.boundaries.clear(); mesh.halfedges.reserve( nHE ); mesh.vertices.reserve( nV ); mesh.edges.reserve( nE ); mesh.faces.reserve( nF ); mesh.boundaries.reserve( nB ); } extern vector isolated; // all isolated vertices point to isolated.begin() int MeshIO :: buildMesh( const MeshData& data, Mesh& mesh ) { map< pair< int, int >, int > edgeCount; map< pair< int, int >, HalfEdgeIter > existingHalfEdges; map< int, VertexIter > indexToVertex; map< HalfEdgeIter, bool > hasFlipEdge; preallocateMeshElements( data, mesh ); // allocate a vertex for each position in the data and construct // a map from vertex indices to vertex pointers for( unsigned int i = 0; i < data.positions.size(); i++ ) { VertexIter newVertex = mesh.vertices.insert( mesh.vertices.end(), Vertex() ); newVertex->position = data.positions[ i ]; newVertex->he = isolated.begin(); indexToVertex[ i ] = newVertex; } // insert each face into the mesh int faceIndex = 0; bool degenerateFaces = false; for( vector< vector< Index > >::const_iterator f = data.indices.begin(); f != data.indices.end(); f ++ ) { int N = f->size(); // print an error if the face is degenerate if( N < 3 ) { cerr << "Error: face " << faceIndex << " is degenerate (fewer than three vertices)!" << endl; degenerateFaces = true; continue; } // create a new face FaceIter newFace = mesh.faces.insert( mesh.faces.end(), Face()); // create a new half edge for each edge of the current face vector< HalfEdgeIter > hes( N ); for( int i = 0; i < N; i++ ) { hes[ i ] = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); } // initialize these new halfedges for( int i = 0; i < N; i++ ) { // the current halfedge goes from vertex a to vertex b int a = (*f)[ i ].position; int b = (*f)[ (i+1) % N ].position; // set current halfedge's attributes hes[ i ]->next = hes[ (i+1) % N ]; hes[ i ]->vertex = indexToVertex[ a ]; int t = (*f)[i].texcoord; if( t >= 0 ) hes[ i ]->texcoord = data.texcoords[ t ]; else hes[ i ]->texcoord = Vector( 0., 0., 0. ); hes[ i ]->onBoundary = false; // keep track of which halfedges have flip edges defined (for detecting boundaries) hasFlipEdge[ hes[ i ]] = false; // point vertex a at the current halfedge indexToVertex[ a ]->he = hes[ i ]; // point the new face and this half edge to each-other hes[ i ]->face = newFace; newFace->he = hes[ i ]; // if we've created an edge between a and b in the past, it is the // flip edge of the current halfedge if( a > b ) swap( a, b ); if( existingHalfEdges.find( pair( a, b )) != existingHalfEdges.end()) { hes[ i ]->flip = existingHalfEdges[ pair( a, b ) ]; hes[ i ]->flip->flip = hes[ i ]; hes[ i ]->edge = hes[ i ]->flip->edge; hasFlipEdge[ hes[ i ]] = true; hasFlipEdge[ hes[ i ]->flip ] = true; } else // otherwise, create an edge connected to the current halfedge { hes[ i ]->edge = mesh.edges.insert( mesh.edges.end(), Edge()); hes[ i ]->edge->he = hes[i]; edgeCount[ pair( a, b ) ] = 0; } // record the fact that we've created a halfedge from a to b existingHalfEdges[ pair( a, b ) ] = hes[ i ]; // check for nonmanifold edges edgeCount[ pair( a, b ) ]++; if( edgeCount[ pair( a, b ) ] > 2 ) { cerr << "Error: edge (" << a << ", " << b << ") is nonmanifold (more than two faces sharing a single edge)!" << endl; return 1; } } faceIndex++; } // give up now if there were degenerate faces if( degenerateFaces ) { return 1; } // insert extra faces for each boundary cycle for( HalfEdgeIter currentHE = mesh.halfedges.begin(); currentHE != mesh.halfedges.end(); currentHE ++ ) { // if we find a halfedge with no flip edge defined, create // a new face and link it to the corresponding boundary cycle if( !hasFlipEdge[ currentHE ] ) { // create a new face FaceIter newBoundary = mesh.boundaries.insert( mesh.boundaries.end(), Face()); // walk along this boundary cycle vector boundaryCycle; HalfEdgeIter he = currentHE; do { // create a new halfedge on the boundary face HalfEdgeIter newHE = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); // mark only the halfedge on the boundary face as being on the boundary newHE->onBoundary = true; // link the current halfedge in the cycle to its new flip edge he->flip = newHE; // grab the next halfedge along the boundary by finding // the next halfedge around the current vertex that doesn't // have a flip edge defined HalfEdgeIter nextHE = he->next; while( hasFlipEdge[ nextHE ] ) { nextHE = nextHE->flip->next; } // set attributes for the flip edge (we'll set ->next below) newHE->flip = he; newHE->vertex = nextHE->vertex; newHE->edge = he->edge; newHE->face = newBoundary; newHE->texcoord = nextHE->texcoord; // point the new face to this half edge newBoundary->he = newHE; // keep track of all the new halfedges in the boundary cycle boundaryCycle.push_back( newHE ); // continue to walk along the cycle he = nextHE; } while( he != currentHE ); // link together the cycle of boundary halfedges unsigned int N = boundaryCycle.size(); for( unsigned int i = 0; i < N; i++ ) { boundaryCycle[ i ]->next = boundaryCycle[ (i+N-1)%N ]; hasFlipEdge[ boundaryCycle[i] ] = true; hasFlipEdge[ boundaryCycle[i]->flip ] = true; } } } // print a warning if the input has any non-terminal defects checkIsolatedVertices( mesh ); checkNonManifoldVertices( mesh ); return 0; } void MeshIO :: readPosition( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.positions.push_back( Vector( x, y, z )); } void MeshIO :: readTexCoord( stringstream& ss, MeshData& data ) { double u, v; ss >> u >> v; data.texcoords.push_back( Vector( u, v, 0. )); } void MeshIO :: readNormal( stringstream& ss, MeshData& data ) { double x, y, z; ss >> x >> y >> z; data.normals.push_back( Vector( x, y, z )); } void MeshIO :: readFace( stringstream& ss, MeshData &data ) { vector faceIndices; string token; while( ss >> token ) { faceIndices.push_back( parseFaceIndex( token )); } data.indices.push_back( faceIndices ); } Index MeshIO :: parseFaceIndex( const string& token ) { // parse indices of the form // // p/[t]/[n] // // where p is an index into positions, t is an index into // texcoords, n is an index into normals, and [.] indicates // that an index is optional stringstream in( token ); string indexstring; int indices[3] = { -1, -1, -1 }; int i = 0; while( getline( in, indexstring, '/' )) { stringstream ss( indexstring ); ss >> indices[i++]; } // decrement since indices in OBJ files are 1-based return Index( indices[0]-1, indices[1]-1, indices[2]-1 ); } void MeshIO :: checkIsolatedVertices( const Mesh& mesh ) { // print a warning if the mesh has any isolated vertices int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { cerr << "Warning: vertex " << vertexIndex << " is isolated (not contained in any face)." << endl; } vertexIndex++; } } void MeshIO :: checkNonManifoldVertices( const Mesh& mesh ) { map nIncidentFaces; for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } for( FaceCIter f = mesh.boundaries.begin(); f != mesh.boundaries.end(); f ++ ) { HalfEdgeCIter he = f->he; do { nIncidentFaces[he->vertex]++; he = he->next; } while( he != f->he ); } int vertexIndex = 0; for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( nIncidentFaces[v] != v->valence() ) { cerr << "Warning: vertex " << vertexIndex << " is nonmanifold." << endl; } vertexIndex++; } } } ================================================ FILE: Hot2/src/Quaternion.cpp ================================================ #include #include using namespace std; #include "Quaternion.h" namespace DDG { // CONSTRUCTORS ---------------------------------------------------------- Quaternion :: Quaternion( void ) // initializes all components to zero : s( 0. ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Quaternion& q ) // initializes from existing quaternion : s( q.s ), v( q.v ) {} Quaternion :: Quaternion( double s_, double vi, double vj, double vk ) // initializes with specified double (s) and imaginary (v) components : s( s_ ), v( vi, vj, vk ) {} Quaternion :: Quaternion( double s_, const Vector& v_ ) // initializes with specified double(s) and imaginary (v) components : s( s_ ), v( v_ ) {} Quaternion :: Quaternion( double s_ ) // initializes purely real quaternion with specified real (s) component (imaginary part is zero) : s( s_ ), v( 0., 0., 0. ) {} Quaternion :: Quaternion( const Vector& v_ ) // initializes purely imaginary quaternion with specified imaginary (v) component (real part is zero) : s( 0. ), v( v_ ) {} Quaternion :: Quaternion( const Complex& z ) // for a complex number z=a+bi, initializes quaternion to a+bi+0j+0k : s( z.re ), v( z.im, 0., 0. ) {} // ASSIGNMENT OPERATORS -------------------------------------------------- const Quaternion& Quaternion :: operator=( double _s ) // assigns a purely real quaternion with real value s { s = _s; v = Vector( 0., 0., 0. ); return *this; } const Quaternion& Quaternion :: operator=( const Vector& _v ) // assigns a purely real quaternion with imaginary value v { s = 0.; v = _v; return *this; } // ACCESSORS ------------------------------------------------------------- double& Quaternion::operator[]( int index ) // returns reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } const double& Quaternion::operator[]( int index ) const // returns const reference to the specified component (0-based indexing: double, i, j, k) { return ( &s )[ index ]; } void Quaternion::toMatrix( double Q[4][4] ) const // returns 4x4 matrix representation { Q[0][0] = s; Q[0][1] = -v.x; Q[0][2] = -v.y; Q[0][3] = -v.z; Q[1][0] = v.x; Q[1][1] = s; Q[1][2] = -v.z; Q[1][3] = v.y; Q[2][0] = v.y; Q[2][1] = v.z; Q[2][2] = s; Q[2][3] = -v.x; Q[3][0] = v.z; Q[3][1] = -v.y; Q[3][2] = v.x; Q[3][3] = s; } double& Quaternion::re( void ) // returns reference to double part { return s; } const double& Quaternion::re( void ) const // returns const reference to double part { return s; } Vector& Quaternion::im( void ) // returns reference to imaginary part { return v; } const Vector& Quaternion::im( void ) const // returns const reference to imaginary part { return v; } // VECTOR SPACE OPERATIONS ----------------------------------------------- Quaternion Quaternion::operator+( const Quaternion& q ) const // addition { return Quaternion( s+q.s, v+q.v ); } Quaternion Quaternion::operator-( const Quaternion& q ) const // subtraction { return Quaternion( s-q.s, v-q.v ); } Quaternion Quaternion::operator-( void ) const // negation { return Quaternion( -s, -v ); } Quaternion Quaternion::operator*( double c ) const // scalar multiplication { return Quaternion( s*c, v*c ); } Quaternion operator*( double c, const Quaternion& q ) // scalar multiplication { return q*c; } Quaternion Quaternion::operator/( double c ) const // scalar division { return Quaternion( s/c, v/c ); } void Quaternion::operator+=( const Quaternion& q ) // addition / assignment { s += q.s; v += q.v; } void Quaternion::operator+=( double c ) // addition / assignment of pure real { s += c; } void Quaternion::operator-=( const Quaternion& q ) // subtraction / assignment { s -= q.s; v -= q.v; } void Quaternion::operator-=( double c ) // subtraction / assignment of pure real { s -= c; } void Quaternion::operator*=( double c ) // scalar multiplication / assignment { s *= c; v *= c; } void Quaternion::operator/=( double c ) // scalar division / assignment { s /= c; v /= c; } // ALGEBRAIC OPERATIONS -------------------------------------------------- Quaternion Quaternion::operator*( const Quaternion& q ) const // Hamilton product { const double& s1( s ); const double& s2( q.s ); const Vector& v1( v ); const Vector& v2( q.v ); return Quaternion( s1*s2 - dot(v1,v2), s1*v2 + s2*v1 + cross(v1,v2) ); } void Quaternion::operator*=( const Quaternion& q ) // Hamilton product / assignment { *this = ( *this * q ); } Quaternion Quaternion::conj( void ) const // conjugation { return Quaternion( s, -v ); } Quaternion Quaternion::inv( void ) const { return ( this->conj() ) / this->norm2(); } // NORMS ----------------------------------------------------------------- double Quaternion::norm( void ) const // returns Euclidean length { return sqrt( s*s + v.x*v.x + v.y*v.y + v.z*v.z ); } double Quaternion::norm2( void ) const // returns Euclidean length squared { return s*s + dot(v,v); } Quaternion Quaternion::unit( void ) const // returns unit quaternion { return *this / norm(); } void Quaternion::normalize( void ) // divides by Euclidean length { *this /= norm(); } // GEOMETRIC OPERATIONS -------------------------------------------------- Quaternion slerp( const Quaternion& q0, const Quaternion& q1, double t ) // spherical-linear interpolation { // interpolate length double m0 = q0.norm(); double m1 = q1.norm(); double m = (1-t)*m0 + t*m1; // interpolate direction Quaternion p0 = q0 / m0; Quaternion p1 = q1 / m1; double theta = acos(( p0.conj()*p1 ).re() ); Quaternion p = ( sin((1-t)*theta)*p0 + sin(t*theta)*p1 )/sin(theta); return m*p; } // I/O ------------------------------------------------------------------------- std::ostream& operator<<( std::ostream& os, const Quaternion& q ) // prints components { os << "( " << q.re() << ", " << q.im() << " )"; return os; } } ================================================ FILE: Hot2/src/Real.cpp ================================================ #include "Real.h" #include namespace DDG { Real :: Real( double x ) // constructs real number with value x : value( x ) {} Real :: operator double( void ) const // type cast to double { return value; } void Real :: operator+=( double x ) // increment { value += x; } void Real :: operator-=( double x ) // decrement { value -= x; } void Real :: operator*=( double x ) // multiply { value *= x; } void Real :: operator/=( double x ) // divide { value /= x; } Real Real :: conj( void ) const // simply returns the value (for compatibility w/ complex numbers) { return value; } Real Real :: inv( void ) const // returns inverse { return 1. / value; } double Real :: norm( void ) const // returns norm { return fabs( value ); } double Real :: norm2( void ) const // returns norm squared { return value * value; } Real Real :: unit( void ) const // returns number with unit norm and same sign { return value / norm(); } } ================================================ FILE: Hot2/src/Shader.cpp ================================================ #include "Shader.h" #include #include using namespace std; namespace DDG { Shader::Shader( void ) // constructor -- shader is initially invalid : vertexShader( 0 ), fragmentShader( 0 ), geometryShader( 0 ), program( 0 ), linked( false ) {} Shader::~Shader( void ) { if( program ) glDeleteProgram( program ); if( vertexShader ) glDeleteShader( vertexShader ); if( fragmentShader ) glDeleteShader( fragmentShader ); if( geometryShader ) glDeleteShader( geometryShader ); } void Shader::loadVertex( const char* filename ) { load( GL_VERTEX_SHADER, filename, vertexShader ); } void Shader::loadFragment( const char* filename ) { load( GL_FRAGMENT_SHADER, filename, fragmentShader ); } void Shader::loadGeometry( const char* filename ) { #ifdef GL_GEOMETRY_SHADER_EXT load( GL_GEOMETRY_SHADER_EXT, filename, geometryShader ); #else cerr << "Error: geometry shaders not supported!" << endl; #endif } void Shader::enable( void ) { if( !linked ) { glLinkProgram( program ); linked = true; } glUseProgram( program ); } void Shader::disable( void ) const { glUseProgram( 0 ); } Shader::operator GLuint( void ) const { return program; } void Shader::load( GLenum shaderType, const char* filename, GLuint& shader ) // read vertex shader from GLSL source file, compile, and attach to program { string source; if( !readSource( filename, source )) { return; } if( program == 0 ) { program = glCreateProgram(); } if( shader != 0 ) { glDetachShader( program, shader ); } shader = glCreateShader( shaderType ); const char* source_c_str = source.c_str(); glShaderSource( shader, 1, &(source_c_str), NULL ); glCompileShader( shader ); GLint compileStatus; glGetShaderiv( shader, GL_COMPILE_STATUS, &compileStatus ); if( compileStatus == GL_TRUE ) { glAttachShader( program, shader ); linked = false; } else { GLsizei maxLength = 0; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength ); if( maxLength > 0 ) { GLchar* infoLog = new char[ maxLength ]; GLsizei length; glGetShaderInfoLog( shader, maxLength, &length, infoLog ); cerr << "GLSL Error: " << infoLog << endl; delete[] infoLog; } } } bool Shader::readSource( const char* filename, std::string& source ) // reads GLSL source file into a string { source = ""; ifstream in( filename ); if( !in.is_open() ) { cerr << "Error: could not open shader file "; cerr << filename; cerr << " for input!" << endl; return false; } string line; while( getline( in, line )) { source += line; } return true; } } ================================================ FILE: Hot2/src/SparseMatrix.cpp ================================================ #include "SparseMatrix.h" namespace DDG { template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_REAL ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = pr[k]; } } return *this; } template <> const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) { assert( B ); assert( B->xtype == CHOLMOD_COMPLEX ); if( cData ) { cholmod_l_free_sparse( &cData, context ); } cData = B; m = cData->nrow; n = cData->ncol; resize( m, n ); double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; // iterate over columns for( int col = 0; col < n; col++ ) { // iterate over nonzero rows for( int k = jc[col]; k < jc[col+1]; k++ ) { int row = ir[k]; (*this)( row, col ) = Complex( pr[k*2+0], pr[k*2+1] ); } } return *this; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ) { SparseMatrix A( m*4, n*4 ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; const Quaternion& q( e->second ); A(i*4+0,j*4+0) = q[0]; A(i*4+0,j*4+1) = -q[1]; A(i*4+0,j*4+2) = -q[2]; A(i*4+0,j*4+3) = -q[3]; A(i*4+1,j*4+0) = q[1]; A(i*4+1,j*4+1) = q[0]; A(i*4+1,j*4+2) = -q[3]; A(i*4+1,j*4+3) = q[2]; A(i*4+2,j*4+0) = q[2]; A(i*4+2,j*4+1) = q[3]; A(i*4+2,j*4+2) = q[0]; A(i*4+2,j*4+3) = -q[1]; A(i*4+3,j*4+0) = q[3]; A(i*4+3,j*4+1) = -q[2]; A(i*4+3,j*4+2) = q[1]; A(i*4+3,j*4+3) = q[0]; } if( cData != NULL ) { cholmod_l_free_sparse( &cData, context ); } cData = cholmod_l_copy_sparse( A.to_cholmod(), context ); return cData; } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_COMPLEX, context ); } template <> void SparseMatrix :: allocateSparse( void ) { int nzmax = data.size(); int sorted = true; int packed = true; int stype = 0; cData = cholmod_l_allocate_sparse( m*4, n*4, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i] = e->second; } template <> void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) { pr[i*2+0] = e->second.re; pr[i*2+1] = e->second.im; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR< complex >( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (complex)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; } template <> void solve( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse QR factorization { int t0 = clock(); x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); int t1 = clock(); cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (quaternion)" << "\n"; cout << "[qr] rank: " << (*context).SPQR_istat[4]/4 << "\n"; } template <> void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_zl_symbolic( n, n, Ap, Ai, Ax, NULL, &Symbolic, NULL, NULL ); umfpack_zl_numeric( Ap, Ai, Ax, NULL, Symbolic, &Numeric, NULL, NULL ); umfpack_zl_solve( UMFPACK_A, Ap, Ai, Ax, NULL, (double*) &x(0), NULL, (double*) &b(0), NULL, Numeric, NULL, NULL ); umfpack_zl_free_symbolic( &Symbolic ); umfpack_zl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } } ================================================ FILE: Hot2/src/SparseMatrix.inl ================================================ #include #include #include #include #include using namespace std; #include #include #include "Real.h" #include "Complex.h" #include "SparseMatrix.h" #include "DenseMatrix.h" #include "LinearContext.h" #include "Utility.h" namespace DDG { extern LinearContext context; const int maxEigIter = 20; // number of iterations used to solve eigenvalue problems template SparseMatrix :: SparseMatrix( int m_, int n_ ) // initialize an mxn matrix : m( m_ ), n( n_ ), cData( NULL ) {} template SparseMatrix :: SparseMatrix( const SparseMatrix& B ) // copy constructor : cData( NULL ) { *this = B; } template SparseMatrix :: ~SparseMatrix( void ) // destructor { if( cData ) { cholmod_l_free_sparse( &cData, context ); } } template const SparseMatrix& SparseMatrix :: operator=( const SparseMatrix& B ) // copies B { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } m = B.m; n = B.n; data = B.data; return *this; } template SparseMatrix SparseMatrix :: transpose( void ) const { SparseMatrix AT( n, m ); for( const_iterator e = begin(); e != end(); e++ ) { int i = e->first.second; int j = e->first.first; T Aij = e->second; AT(j,i) = Aij.conj(); } return AT; } template SparseMatrix SparseMatrix :: operator*( const SparseMatrix& B ) const // returns product of this matrix with sparse B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // collect nonzeros in each row vector< vector< int > > Bcol( B.nRows() ); vector< vector< T > > Bval( B.nRows() ); for( const_iterator e = B.begin(); e != B.end(); e ++ ) { int row = e->first.second; int col = e->first.first; T val = e->second; Bcol[ row ].push_back( col ); Bval[ row ].push_back( val ); } // multiply C = A*B SparseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( size_t n = 0; n < Bcol[j].size(); n++ ) { int k = Bcol[j][n]; C( i, k ) += e->second * Bval[j][n]; } } return C; } template DenseMatrix SparseMatrix :: operator*( const DenseMatrix& B ) const // returns product of this matrix with dense B { const SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nColumns() == B.nRows() ); // multiply C = A*B DenseMatrix C( A.nRows(), B.nColumns() ); for( const_iterator e = begin(); e != end(); e ++ ) { int i = e->first.second; int j = e->first.first; for( int k = 0; k < B.nColumns(); k++ ) { C( i, k ) += e->second * B( j, k ); } } return C; } template void SparseMatrix :: operator*=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second *= c; } } template void SparseMatrix :: operator/=( const T& c ) { for( iterator e = begin(); e != end(); e++ ) { e->second /= c; } } template void SparseMatrix :: operator+=( const SparseMatrix& B ) // adds B to this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) += Bij; } } template void SparseMatrix :: operator-=( const SparseMatrix& B ) // subtracts B from this matrix { SparseMatrix& A( *this ); // make sure matrix dimensions agree assert( A.nRows() == B.nRows() ); assert( A.nColumns() == B.nColumns() ); for( const_iterator e = B.begin(); e != B.end(); e++ ) { int i = e->first.second; int j = e->first.first; const T& Bij( e->second ); A( i, j ) -= Bij; } } template SparseMatrix SparseMatrix :: operator+( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C += B; return C; } template SparseMatrix SparseMatrix :: operator-( const SparseMatrix& B ) const // returns sum of this matrix with B { SparseMatrix C( nRows(), nColumns() ); C += *this; C -= B; return C; } template SparseMatrix operator*( const T& c, const SparseMatrix& A ) { SparseMatrix cA = A; for( typename SparseMatrix::iterator e = cA.begin(); e != cA.end(); e++ ) { e->second = c * e->second; } return cA; } template SparseMatrix operator*( const SparseMatrix& A, const T& c ) { SparseMatrix Ac = A; Ac *= c; return Ac; } template SparseMatrix operator/( const SparseMatrix& A, T c ) { SparseMatrix Ac = A; Ac /= c; return Ac; } template void SparseMatrix :: resize( int m_, int n_ ) { m = m_; n = n_; data.clear(); } template int SparseMatrix :: nRows( void ) const // returns the number of rows { return m; } template int SparseMatrix :: nColumns( void ) const // returns the number of columns { return n; } template int SparseMatrix :: length( void ) const // returns the size of the largest dimension { return max( m, n ); } template void SparseMatrix :: zero( const T& val ) // sets all nonzero elements val { for( iterator i = begin(); i != end(); i++ ) { i->second = val; } } template SparseMatrix SparseMatrix :: inverse( void ) const // returns inverse -- for diagonal matrices only { assert( m == n ); // matrix must be square const SparseMatrix& A( *this ); SparseMatrix Ainv( m, m ); for( const_iterator e = begin(); e != end(); e++ ) { int r = e->first.second; int c = e->first.first; assert( r == c ); // matrix must be diagonal Ainv( r, c ) = A( r, c ).inv(); } return Ainv; } template SparseMatrix SparseMatrix :: identity( int N ) { SparseMatrix I( N, N ); for( int i = 0; i < N; i++ ) { I( i, i ) = 1.; } return I; } template DenseMatrix SparseMatrix :: full( void ) const // converts to a dense matrix { const int maxSize = 1048576; if( m*n > maxSize ) { cerr << "Error: refusing to convert sparse to dense (too big!)" << "\n"; exit( 1 ); } const SparseMatrix& A( *this ); DenseMatrix B( m, n ); for( int i = 0; i < m; i++ ) for( int j = 0; j < m; j++ ) { B( i, j ) = A( i, j ); } return B; } template cholmod_sparse* SparseMatrix :: to_cholmod( void ) { if( cData ) { cholmod_l_free_sparse( &cData, context ); cData = NULL; } allocateSparse(); // build compressed matrix (note that EntryMap stores entries in column-major order) double* pr = (double*) cData->x; UF_long* ir = (UF_long*) cData->i; UF_long* jc = (UF_long*) cData->p; int i = 0; int j = -1; for( const_iterator e = begin(); e != end(); e ++ ) { int c = e->first.first; if( c != j ) { for( int k = j+1; k <= c; k++ ) { jc[k] = i; } j = c; } ir[i] = e->first.second; setEntry( e, i, pr ); i++; } for( int k = j+1; k <= n; k++ ) { jc[k] = i; } return cData; } template <> cholmod_sparse* SparseMatrix :: to_cholmod( void ); template T& SparseMatrix :: operator()( int row, int col ) { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { data[ index ] = T( 0. ); } return data[ index ]; } template T SparseMatrix :: operator()( int row, int col ) const { EntryIndex index( col, row ); const_iterator entry = data.find( index ); if( entry == end()) { return T( 0. ); } return entry->second; } template typename SparseMatrix::iterator SparseMatrix :: begin( void ) { return data.begin(); } template typename SparseMatrix::const_iterator SparseMatrix :: begin( void ) const { return data.begin(); } template typename SparseMatrix::iterator SparseMatrix :: end( void ) { return data.end(); } template typename SparseMatrix::const_iterator SparseMatrix :: end( void ) const { return data.end(); } template void SparseMatrix :: shift( double c ) // adds c times the identity matrix to this matrix { assert( m == n ); SparseMatrix& A( *this ); for( int i = 0; i < m; i++ ) { A( i, i ) += c; } } template void solveSymmetric( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the sparse linear system Ax = b using sparse LU factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); int n = Ac->nrow; UF_long* Ap = (UF_long*) Ac->p; UF_long* Ai = (UF_long*) Ac->i; double* Ax = (double*) Ac->x; void* Symbolic; void* Numeric; umfpack_dl_symbolic( n, n, Ap, Ai, Ax, &Symbolic, NULL, NULL ); umfpack_dl_numeric( Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL ); umfpack_dl_solve( UMFPACK_A, Ap, Ai, Ax, (double*) &x(0), (double*) &b(0), Numeric, NULL, NULL ); umfpack_dl_free_symbolic( &Symbolic ); umfpack_dl_free_numeric( &Numeric ); int t1 = clock(); cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; } template void solvePositiveDefinite( SparseMatrix& A, DenseMatrix& x, DenseMatrix& b ) // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization { int t0 = clock(); cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; cholmod_factor* L = cholmod_l_analyze( Ac, context ); cholmod_l_factorize( Ac, L, context ); x = cholmod_l_solve( CHOLMOD_A, L, b.to_cholmod(), context ); if( L ) cholmod_l_free_factor( &L, context ); int t1 = clock(); cout << "[chol] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[chol] max residual: " << residual( A, x, b ) << "\n"; } template void backsolvePositiveDefinite( SparseFactor& L, DenseMatrix& x, DenseMatrix& b ) // backsolves the prefactored positive definite sparse linear system LL'x = b { x = cholmod_l_solve( CHOLMOD_A, L.to_cholmod(), b.to_cholmod(), context ); } template void smallestEig( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be symmetric; x is used as an initial guess { int t0 = clock(); for( int iter = 0; iter < maxEigIter; iter++ ) { solve( A, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEig( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda // A and B must be symmetric; x is used as an initial guess { // TODO use a symmetric matrix decomposition instead of QR int t0 = clock(); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= dot( e, B*e ).norm(); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; solve( A, x, x ); x -= dot( x, Be ).conj()*e; x /= dot( x, B*x ).norm(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, DenseMatrix& x, bool ignoreConstantVector ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); for( int iter = 0; iter < maxEigIter; iter++ ) { backsolvePositiveDefinite( L, x, x ); if( ignoreConstantVector ) { x.removeMean(); } x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& x ) // solves A x = lambda x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite; x is used as an initial guess { int t0 = clock(); SparseFactor L; L.build( A ); // create vector e that has unit norm w.r.t. B int n = A.length(); DenseMatrix e( n, 1 ); e.zero( 1. ); e /= sqrt( dot( e, B*e ).norm() ); DenseMatrix Be = B*e; for( int iter = 0; iter < maxEigIter; iter++ ) { x = B*x; backsolvePositiveDefinite( L, x, x ); x -= dot( x, Be ).conj()*e; x /= sqrt( dot( x, B*x ).norm() ); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, x ) << "\n"; } template void smallestEigPositiveDefinite( SparseMatrix& A, SparseMatrix& B, DenseMatrix& E, DenseMatrix& x ) // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and // x is used as an initial guess { int iter; int t0 = clock(); DenseMatrix ET = E.transpose(); SparseFactor L; L.build( A ); for( iter = 0; iter < maxEigIter; iter++ ) { x = B*x - E*(ET*x); backsolvePositiveDefinite( L, x, x ); x.normalize(); } int t1 = clock(); cout << "[eig] time: " << seconds( t0, t1 ) << "s" << "\n"; cout << "[eig] max residual: " << residual( A, B, E, x ) << "\n"; } template double residual( const SparseMatrix& A, const DenseMatrix& x, const DenseMatrix& b ) // returns the max residual of the linear problem A x = b relative to the largest entry of the solution { return ( A*x - b ).norm() / b.norm(); } template double residual( const SparseMatrix& A, const DenseMatrix& x ) // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, x ); return (A*x-lambda*x).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, x ); return (A*x-lambda*(B*x)).norm() / x.norm(); } template double residual( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution { T lambda = rayleighQuotient( A, B, E, x ); return (A*x-lambda*(B*x-E*(E.transpose()*x))).norm() / x.norm(); } template T rayleighQuotient( const SparseMatrix& A, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*x)(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& x ) // returns / { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x))(0).inv(); } template T rayleighQuotient( const SparseMatrix& A, const SparseMatrix& B, const DenseMatrix& E, const DenseMatrix& x ) // returns /<(B-EE^T)x,x> { return (x.transpose()*(A*x))(0) * (x.transpose()*(B*x-E*(E.transpose()*x)))(0).inv(); } template std::ostream& operator<<( std::ostream& os, const SparseMatrix& o) { os.precision( 3 ); for( typename SparseMatrix::const_iterator e = o.begin(); e != o.end(); e ++ ) { int row = e->first.second; int col = e->first.first; os << "( " << row << ", " << col << " ): " << e->second << "\n"; } return os; } template SparseFactor :: SparseFactor( void ) : L( NULL ) {} template SparseFactor :: ~SparseFactor( void ) { if( L ) { cholmod_l_free_factor( &L, context ); } } template void SparseFactor :: build( SparseMatrix& A ) { if( L ) { cholmod_l_free_factor( &L, context ); L = NULL; } int t0, t1; cholmod_sparse* Ac = A.to_cholmod(); Ac->stype = 1; t0 = clock(); L = cholmod_l_analyze( Ac, context ); t1 = clock(); cerr << "analyze: " << seconds(t0,t1) << "s" << endl; t0 = clock(); cholmod_l_factorize( Ac, L, context ); t1 = clock(); cerr << "factorize: " << seconds(t0,t1) << "s" << endl; } template bool SparseFactor :: valid( void ) const { if( L == NULL ) { return false; } return true; } template cholmod_factor* SparseFactor :: to_cholmod( void ) { return L; } } ================================================ FILE: Hot2/src/Variable.cpp ================================================ #include "Variable.h" namespace DDG { Variable :: Variable( double value_, bool fixed_ ) // initialize a variable which has value zero and is not fixed by default : value( value_ ), fixed( fixed_ ) {} Variable :: Variable( std::string name_, double value_, bool fixed_ ) // initialize a named variable which has value zero and is not fixed by default : name( name_ ), value( value_ ), fixed( fixed_ ) {} double& Variable :: operator*( void ) // returns a reference to the numerical value { return value; } const double& Variable :: operator*( void ) const // returns a const reference to the numerical value { return value; } } ================================================ FILE: Hot2/src/Vector.cpp ================================================ #include #include "Vector.h" namespace DDG { Vector :: Vector( void ) : x( 0. ), y( 0. ), z( 0. ) {} Vector :: Vector( double x0, double y0, double z0 ) : x( x0 ), y( y0 ), z( z0 ) {} Vector :: Vector( const Vector& v ) : x( v.x ), y( v.y ), z( v.z ) {} double& Vector :: operator[]( const int& index ) { return ( &x )[ index ]; } const double& Vector :: operator[]( const int& index ) const { return ( &x )[ index ]; } Vector Vector :: operator+( const Vector& v ) const { return Vector( x + v.x, y + v.y, z + v.z ); } Vector Vector :: operator-( const Vector& v ) const { return Vector( x - v.x, y - v.y, z - v.z ); } Vector Vector :: operator-( void ) const { return Vector( -x, -y, -z ); } Vector Vector :: operator*( const double& c ) const { return Vector( x*c, y*c, z*c ); } Vector operator*( const double& c, const Vector& v ) { return v*c; } Vector Vector :: operator/( const double& c ) const { return (*this) * ( 1./c ); } void Vector :: operator+=( const Vector& v ) { x += v.x; y += v.y; z += v.z; } void Vector :: operator-=( const Vector& v ) { x -= v.x; y -= v.y; z -= v.z; } void Vector :: operator*=( const double& c ) { x *= c; y *= c; z *= c; } void Vector :: operator/=( const double& c ) { (*this) *= ( 1./c ); } double Vector :: norm( void ) const { return sqrt( norm2()); } double Vector :: norm2( void ) const { return dot( *this, *this ); } void Vector :: normalize( void ) { (*this) /= norm(); } Vector Vector :: unit( void ) const { return (*this) / norm(); } Vector Vector :: abs( void ) const { return Vector( fabs( x ), fabs( y ), fabs( z ) ); } double dot( const Vector& u, const Vector& v ) { return u.x*v.x + u.y*v.y + u.z*v.z ; } Vector cross( const Vector& u, const Vector& v ) { return Vector( u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x ); } std::ostream& operator << (std::ostream& os, const Vector& o) { os << "[ " << o.x << " " << o.y << " " << o.z << " ]"; return os; } } ================================================ FILE: Hot2/src/Vertex.cpp ================================================ #include using namespace std; #include "Vertex.h" #include "Mesh.h" #include "HalfEdge.h" namespace DDG { double Vertex::area( void ) const // returns the dual area associated with this vertex { double A = 0.; HalfEdgeCIter h = he; do { if (not h->onBoundary) A += h->face->area(); h = h->flip->next; } while( h != he ); return A / 3.; } Vector Vertex::normal( void ) const // returns the vertex normal { Vector N; HalfEdgeCIter h = he; do { if (not h->onBoundary) N += h->face->normal(); h = h->flip->next; } while( h != he ); return N.unit(); } vector isolated; // all isolated vertices point to isolated.begin() bool Vertex::isIsolated( void ) const // returns true if the vertex is not contained in any face or edge; false otherwise { return he == isolated.begin(); } int Vertex :: valence( void ) const // returns the number of incident faces { int n = 0; HalfEdgeCIter h = he; do { n++; h = h->flip->next; } while( h != he ); return n; } void Vertex :: toggleTag() { tag = !tag; } } ================================================ FILE: Hot2/src/Viewer.cpp ================================================ #include #include #include #include #include #include #include using namespace std; #include "Viewer.h" #include "Image.h" #include "Application.h" namespace DDG { // declare static member variables Mesh Viewer::mesh; GLuint Viewer::surfaceDL = 0; int Viewer::windowSize[2] = { 512, 512 }; Camera Viewer::camera; Shader Viewer::shader; bool Viewer::renderWireframe = false; bool Viewer::renderDualMesh = true; bool Viewer::renderWeights = false; void Viewer :: init( void ) { restoreViewerState(); initGLUT(); setGL(); initGLSL(); updateDisplayList(); glutMainLoop(); } void Viewer :: initGLUT( void ) { int argc = 0; vector< vector > argv(1); // initialize window glutInitWindowSize( windowSize[0], windowSize[1] ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); glutInit( &argc, (char**)&argv ); glutCreateWindow( "DDG" ); // specify callbacks glutDisplayFunc ( Viewer::display ); glutIdleFunc ( Viewer::idle ); glutKeyboardFunc ( Viewer::keyboard ); glutSpecialFunc ( Viewer::special ); glutMouseFunc ( Viewer::mouse ); glutMotionFunc ( Viewer::motion ); // initialize menus int viewMenu = glutCreateMenu( Viewer::view ); glutSetMenu( viewMenu ); glutAddMenuEntry( "[f] Wireframe", menuWireframe ); glutAddMenuEntry( "[↑] Zoom In", menuZoomIn ); glutAddMenuEntry( "[↓] Zoom Out", menuZoomOut ); glutAddMenuEntry( "[d] Dual Mesh", menuDualMesh ); glutAddMenuEntry( "[s] Weights", menuWeights ); int mainMenu = glutCreateMenu( Viewer::menu ); glutSetMenu( mainMenu ); glutAddMenuEntry( "[space] Process Mesh", menuProcess ); glutAddMenuEntry( "[r] Reset Mesh", menuResetMesh ); glutAddMenuEntry( "[w] Write Mesh", menuWriteMesh ); glutAddMenuEntry( "[\\] Screenshot", menuScreenshot ); glutAddMenuEntry( "[esc] Exit", menuExit ); glutAddSubMenu( "View", viewMenu ); glutAttachMenu( GLUT_RIGHT_BUTTON ); } void Viewer :: initGLSL( void ) { shader.loadVertex( "shaders/vertex.glsl" ); shader.loadFragment( "shaders/fragment.glsl" ); } void Viewer :: menu( int value ) { switch( value ) { case( menuProcess ): mProcess(); break; case( menuResetMesh ): mResetMesh(); break; case( menuWriteMesh ): mWriteMesh(); break; case( menuScreenshot ): mScreenshot(); break; case( menuExit ): mExit(); break; default: break; } } void Viewer :: view( int value ) { switch( value ) { case( menuWireframe ): mWireframe(); break; case( menuZoomIn ): mZoomIn(); break; case( menuZoomOut ): mZoomOut(); break; case( menuDualMesh ): mDualMesh(); break; case( menuWeights ): mWeights(); break; default: break; } } void Viewer :: keyboard( unsigned char c, int x, int y ) { switch( c ) { case 'f': mWireframe(); break; case 'w': mWriteMesh(); break; case 'r': mResetMesh(); break; case '\\': mScreenshot(); break; case ' ': mProcess(); break; case 'd': mDualMesh(); break; case 27: mExit(); break; case 's': mWeights(); break; default: break; } } void Viewer :: special( int i, int x, int y ) { switch( i ) { case GLUT_KEY_UP: camera.zoomIn(); break; case GLUT_KEY_DOWN: camera.zoomOut(); break; case 27: mExit(); break; default: break; } } void Viewer :: mouse( int button, int state, int x, int y ) { if( ( glutGetModifiers() & GLUT_ACTIVE_SHIFT) and state == GLUT_UP ) pickVertex(x, y); else camera.mouse( button, state, x, y ); } void Viewer :: motion( int x, int y ) { camera.motion( x, y ); } void Viewer :: idle( void ) { camera.idle(); glutPostRedisplay(); } void Viewer :: storeViewerState( void ) { ofstream out( ".viewer_state.txt" ); out << camera.rLast[0] << endl; out << camera.rLast[1] << endl; out << camera.rLast[2] << endl; out << camera.rLast[3] << endl; GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); out << view[2] << endl; out << view[3] << endl; } void Viewer :: restoreViewerState( void ) { ifstream in( ".viewer_state.txt" ); if( !in.is_open() ) return; in >> camera.rLast[0]; in >> camera.rLast[1]; in >> camera.rLast[2]; in >> camera.rLast[3]; in >> windowSize[0]; in >> windowSize[1]; } void Viewer :: mProcess( void ) { Application app; app.optimizeWeights(mesh); updateDisplayList(); } void Viewer :: mWeights( void ) { renderWeights = !renderWeights; updateDisplayList(); } void Viewer :: mDualMesh( void ) { renderDualMesh = !renderDualMesh; updateDisplayList(); } void Viewer :: mResetMesh( void ) { mesh.reload(); updateDisplayList(); } void Viewer :: mWriteMesh( void ) { mesh.write( "out.obj" ); } void Viewer :: mExit( void ) { //storeViewerState(); exit( 0 ); } void Viewer :: mWireframe( void ) { renderWireframe = !renderWireframe; updateDisplayList(); } void Viewer :: mZoomIn( void ) { camera.zoomIn(); } void Viewer :: mZoomOut( void ) { camera.zoomOut(); } void Viewer :: mScreenshot( void ) { static int index = 0; // get window width and height GLint view[4]; glGetIntegerv( GL_VIEWPORT, view ); int w = view[2]; int h = view[3]; // get pixels Image image( w, h ); glReadPixels( 0, 0, w, h, GL_BGR, GL_FLOAT, &image(0,0) ); stringstream filename; filename << "frames/viewer" << setw(8) << setfill( '0' ) << index << ".tga"; image.write( filename.str().c_str() ); index++; } void Viewer :: display( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); shader.enable(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); double aspect = (double) viewport[2] / (double) viewport[3]; const double fovy = 50.; const double clipNear = .01; const double clipFar = 1000.; gluPerspective( fovy, aspect, clipNear, clipFar ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); Quaternion eye = Vector( 0., 0., -2.5*camera.zoom ); Quaternion center = Vector( 0., 0., 0. ); Quaternion up = Vector( 0., 1., 0. ); gluLookAt( eye[1], eye[2], eye[3], center[1], center[2], center[3], up[1], up[2], up[3] ); Quaternion r = camera.currentRotation(); eye = r.conj() * eye * r; GLint uniformEye = glGetUniformLocation( shader, "eye" ); glUniform3f( uniformEye, eye[1], eye[2], eye[3] ); Quaternion light = Vector( -1., 1., -2. ); light = r.conj() * light * r; GLint uniformLight = glGetUniformLocation( shader, "light" ); glUniform3f( uniformLight, light[1], light[2], light[3] ); camera.setView(); callDisplayList(); shader.disable(); glutSwapBuffers(); } void Viewer :: updateDisplayList( void ) { if( surfaceDL ) { glDeleteLists( surfaceDL, 1 ); surfaceDL = 0; } surfaceDL = glGenLists( 1 ); glNewList( surfaceDL, GL_COMPILE ); setMeshMaterial(); drawScene(); glEndList(); } void Viewer :: setGL( void ) { glClearColor( .5, .5, .5, 1. ); setLighting(); } void Viewer :: setLighting( void ) { GLfloat position[4] = { 20., 30., 40., 0. }; glLightfv( GL_LIGHT0, GL_POSITION, position ); glEnable( GL_LIGHT0 ); glEnable( GL_NORMALIZE ); } void Viewer :: setMeshMaterial( void ) { GLfloat diffuse[4] = { .8, .5, .3, 1. }; GLfloat specular[4] = { .3, .3, .3, 1. }; GLfloat ambient[4] = { .2, .2, .5, 1. }; glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular ); glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient ); glMaterialf ( GL_FRONT_AND_BACK, GL_SHININESS, 16. ); } void Viewer :: callDisplayList( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_DEPTH_TEST ); glEnable( GL_LIGHTING ); glCallList( surfaceDL ); glPopAttrib(); } void Viewer :: drawScene( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1., 1. ); glColor3d( 1., .5, .25 ); drawPolygons(); glDisable( GL_POLYGON_OFFSET_FILL ); shader.disable(); if( renderWireframe ) drawWireframe(); if( renderDualMesh ) drawDualMesh(); if( renderWeights ) drawWeights(); drawIsolatedVertices(); drawSelectedVertices(); glPopAttrib(); } void Viewer :: drawPolygons( void ) { for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { if( f->isBoundary() ) continue; glBegin( GL_POLYGON ); if( renderWireframe ) { Vector N = f->normal(); glNormal3dv( &N[0] ); } HalfEdgeCIter he = f->he; do { if( not renderWireframe ) { Vector N = he->vertex->normal(); glNormal3dv( &N[0] ); } glVertex3dv( &he->vertex->position[0] ); he = he->next; } while( he != f->he ); glEnd(); } } void Viewer :: drawWireframe( void ) { shader.disable(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glDisable( GL_LIGHTING ); glColor4f( 0., 0., 0., 0.5 ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBegin( GL_LINES ); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e ++ ) { glVertex3dv( &e->he->vertex->position[0] ); glVertex3dv( &e->he->flip->vertex->position[0] ); } glEnd(); glPopAttrib(); } void Viewer :: drawIsolatedVertices( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glPointSize( 5 ); glHint( GL_POINT_SMOOTH_HINT, GL_NICEST ); glEnable( GL_POINT_SMOOTH ); glColor3f( 1., 0., 0. ); glBegin( GL_POINTS ); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->isIsolated() ) { glVertex3dv( &v->position[0] ); } } glEnd(); glPopAttrib(); } void Viewer :: drawVertices( void ) { for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { glLoadName(v->index); glBegin(GL_POINTS); glVertex3dv( &v->position[0] ); glEnd(); } } void Viewer :: drawSelectedVertices( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_COLOR_MATERIAL ); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glColor3f( 0., 0.5, 0.5 ); double h = 0.75*mesh.meanEdgeLength(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->tag ) { glPushMatrix(); glTranslated(v->position.x, v->position.y, v->position.z); glutSolidSphere(h, 10, 10); glPopMatrix(); } } glEnd(); glPopAttrib(); } void Viewer :: pickVertex(int x, int y) { int width = glutGet(GLUT_WINDOW_WIDTH ); int height = glutGet(GLUT_WINDOW_HEIGHT); if( x < 0 || x >= width || y < 0 || y >= height ) return; int bufSize = mesh.vertices.size(); GLuint* buf = new GLuint[bufSize]; glSelectBuffer(bufSize, buf); GLint viewport[4]; GLdouble projection[16]; glGetIntegerv( GL_VIEWPORT, viewport ); glGetDoublev(GL_PROJECTION_MATRIX, projection); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix(x, viewport[3]-y, 10, 10, viewport); glMultMatrixd(projection); glMatrixMode(GL_MODELVIEW); glPushMatrix(); drawVertices(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); long hits = glRenderMode(GL_RENDER); int index = -1; double min_z = 1.0e100; for( long i = 0; i < hits; ++i ) { double distance = buf[4*i + 1]; if( distance < min_z ) { index = buf[4*i + 3]; min_z = distance; } } delete[] buf; if (index >= 0) { mesh.vertices[index].toggleTag(); updateDisplayList(); } } void Viewer :: drawDualMesh( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glDisable( GL_LIGHTING ); glColor4f(0.0, 0.5, 0.0, 0.5); glLineWidth(4.0); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f ++ ) { if( f->isBoundary() ) continue; Vector cf = f->dualPoint(); HalfEdgeCIter he = f->he; do { Vector ce = he->edge->dualPoint(); glBegin(GL_LINES); glVertex3dv( &cf[0] ); glVertex3dv( &ce[0] ); glEnd(); he = he->next; } while( he != f->he ); } glPopAttrib(); } void Viewer :: drawWeights( void ) { glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_COLOR_MATERIAL ); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v ++ ) { if( v->weight > 0.0 ) glColor3f(1.,0.,0.); else glColor3f(0.,0.,1.); double radius = sqrt( abs(v->weight ) ); glPushMatrix(); glTranslated(v->position.x, v->position.y, v->position.z); glutSolidSphere(radius, 10, 10); glPopMatrix(); } glEnd(); glPopAttrib(); } } ================================================ FILE: Hot2/src/main.cpp ================================================ #include using namespace std; #include "Viewer.h" #include "DenseMatrix.h" using namespace DDG; int main( int argc, char** argv ) { if( argc != 2 ) { cerr << "usage: " << argv[0] << " in.obj" << endl; return 1; } Viewer viewer; viewer.mesh.read( argv[1] ); viewer.init(); return 0; } ================================================ FILE: Slides/apps-siggraph2013.key ================================================ [File too large to display: 20.7 MB] ================================================ FILE: Slides/theory-siggraph2013.key ================================================ [File too large to display: 37.4 MB]