├── obj └── .gitignore ├── .gitignore ├── interface.jpg ├── shaders ├── vertex.glsl └── fragment.glsl ├── src ├── main.cpp ├── Edge.cpp ├── LinearContext.cpp ├── Real.cpp ├── HalfEdge.cpp ├── Camera.cpp ├── Vertex.cpp ├── commandline.cpp ├── Vector.cpp ├── Shader.cpp ├── Complex.cpp ├── Face.cpp ├── Image.cpp ├── Mesh.cpp ├── DenseMatrix.cpp ├── Quaternion.cpp ├── DenseMatrix.inl ├── SparseMatrix.cpp ├── SectionIntegrals.cpp ├── KVecDir.cpp └── MeshIO.cpp ├── include ├── SectionIntegrals.h ├── Real.h ├── LinearContext.h ├── Edge.h ├── Utility.h ├── Image.h ├── AliasTable.h ├── Shader.h ├── Camera.h ├── Types.h ├── Complex.h ├── HalfEdge.h ├── MeshIO.h ├── Face.h ├── Vertex.h ├── Vector.h ├── Viewer.h ├── DenseMatrix.h ├── Quaternion.h ├── Mesh.h └── SparseMatrix.h ├── Makefile └── README.md /obj/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | obj/ 2 | .*.sw* 3 | fieldgen 4 | .viewer_state.txt 5 | out.obj 6 | -------------------------------------------------------------------------------- /interface.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeometryCollective/fieldgen/HEAD/interface.jpg -------------------------------------------------------------------------------- /shaders/vertex.glsl: -------------------------------------------------------------------------------- 1 | varying vec3 position; 2 | varying vec3 normal; 3 | varying vec3 color; 4 | 5 | void main() 6 | { 7 | gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 8 | 9 | position = gl_Vertex.xyz; 10 | normal = gl_Normal.xyz; 11 | color = gl_Color.xyz; 12 | } 13 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | #include "Viewer.h" 5 | using namespace DDG; 6 | 7 | int main( int argc, char** argv ) 8 | { 9 | if( argc != 2 ) 10 | { 11 | cerr << "usage: " << argv[0] << " in.obj" << endl; 12 | return 1; 13 | } 14 | 15 | Viewer viewer; 16 | viewer.mesh.read( argv[1] ); 17 | viewer.init(); 18 | 19 | return 0; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /include/SectionIntegrals.h: -------------------------------------------------------------------------------- 1 | #ifndef DDG_SECTIONINTEGRALS_H 2 | #define DDG_SECTIONINTEGRALS_H 3 | 4 | #include "Complex.h" 5 | 6 | extern DDG::Complex 7 | DirichletIJ( const double s, const double gii, const double gij, const double gjj ); 8 | 9 | extern double 10 | DirichletII( const double s, const double gjj, const double gjk, const double gkk ); 11 | 12 | extern DDG::Complex 13 | MassIJ( const double s ); 14 | 15 | extern double MassII( void ); 16 | 17 | #endif /* SECTIONINTEGRALS */ 18 | -------------------------------------------------------------------------------- /src/Edge.cpp: -------------------------------------------------------------------------------- 1 | #include "Edge.h" 2 | #include "Mesh.h" 3 | #include "HalfEdge.h" 4 | 5 | namespace DDG 6 | { 7 | // only needed for old code 8 | const Vector Edge::Xvector( void ) const { 9 | return X()->next->vertex->position - X()->vertex->position; 10 | } 11 | 12 | // standard cotan Laplace coeff for this edge 13 | const double Edge::cot( void ) const { 14 | return .5 * ( ( he->onBoundary ? 0 : he->cot() ) + 15 | ( he->flip->onBoundary ? 0 : he->flip->cot() ) ); 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/LinearContext.cpp: -------------------------------------------------------------------------------- 1 | #include "LinearContext.h" 2 | 3 | namespace DDG 4 | { 5 | // global context for linear solvers 6 | LinearContext context; 7 | 8 | LinearContext :: LinearContext( void ) 9 | // constructor 10 | { 11 | cholmod_l_start( &context ); 12 | } 13 | 14 | LinearContext :: ~LinearContext( void ) 15 | // destructor 16 | { 17 | cholmod_l_finish( &context ); 18 | } 19 | 20 | LinearContext :: operator cholmod_common*( void ) 21 | // allows LinearContext to be treated as a cholmod_common* 22 | { 23 | return &context; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /shaders/fragment.glsl: -------------------------------------------------------------------------------- 1 | uniform vec3 eye; 2 | uniform vec3 light; 3 | varying vec3 position; 4 | varying vec3 normal; 5 | varying vec3 color; 6 | 7 | float diffuse( vec3 N, vec3 L ) 8 | { 9 | return max( 0., dot( N, L )); 10 | } 11 | 12 | float specular( vec3 N, vec3 L, vec3 E ) 13 | { 14 | const float shininess = 8.; 15 | vec3 R = 2.*dot(L,N)*N - L; 16 | return pow( max( 0., dot( R, E )), shininess ); 17 | } 18 | 19 | float fresnel( vec3 N, vec3 E ) 20 | { 21 | const float sharpness = 10.; 22 | float NE = max( 0., dot( N, E )); 23 | return pow( sqrt( 1. - NE*NE ), sharpness ); 24 | } 25 | 26 | void main() 27 | { 28 | vec3 N = normalize( normal ); 29 | vec3 L = normalize( light - position ); 30 | vec3 E = normalize( eye - position ); 31 | vec3 R = 2.*dot(L,N)*N - L; 32 | vec3 one = vec3( 1., 1., 1. ); 33 | 34 | gl_FragColor.rgb = diffuse(N,L)*color + .5*specular(N,L,E)*one + .75*fresnel(N,E)*one; 35 | gl_FragColor.a = 1.; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /include/Real.h: -------------------------------------------------------------------------------- 1 | #ifndef DDG_REAL_H 2 | #define DDG_REAL_H 3 | 4 | namespace DDG 5 | { 6 | class Real 7 | { 8 | public: 9 | Real( double x = 0. ); 10 | // constructs real number with value x 11 | 12 | operator double( void ) const; 13 | // type cast to double 14 | 15 | void operator+=( double x ); 16 | // increment 17 | 18 | void operator-=( double x ); 19 | // decrement 20 | 21 | void operator*=( double x ); 22 | // multiply 23 | 24 | void operator/=( double x ); 25 | // divide 26 | 27 | Real conj( void ) const; 28 | // simply returns the value (for compatibility w/ complex numbers) 29 | 30 | Real inv( void ) const; 31 | // returns inverse 32 | 33 | double norm( void ) const; 34 | // returns norm 35 | 36 | double norm2( void ) const; 37 | // returns norm squared 38 | 39 | Real unit( void ) const; 40 | // returns number with unit norm and same sign 41 | 42 | protected: 43 | double value; 44 | // value 45 | }; 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/LinearContext.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // libDDG -- LinearContext.h 3 | // ----------------------------------------------------------------------------- 4 | // 5 | // LinearContext is the global solver context needed to interface with the 6 | // SuiteSparse library. It is essentially a wrapper around cholmod_common. A 7 | // single static instance of LinearContext is declared in LinearContext.cpp and 8 | // is shared by all instances of DenseMatrix, SparseMatrix, and LinearSystem. 9 | // In other words, you shouldn't have to instantiate LinearContext yourself 10 | // unless you're doing something really fancy! 11 | // 12 | 13 | #ifndef DDG_LINEARSOLVERCONTEXT 14 | #define DDG_LINEARSOLVERCONTEXT 15 | 16 | #include 17 | 18 | namespace DDG 19 | { 20 | class LinearContext 21 | { 22 | public: 23 | LinearContext( void ); 24 | // constructor 25 | 26 | ~LinearContext( void ); 27 | // destructor 28 | 29 | operator cholmod_common*( void ); 30 | // allows LinearContext to be treated as a cholmod_common* 31 | 32 | protected: 33 | cholmod_common context; 34 | }; 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/Real.cpp: -------------------------------------------------------------------------------- 1 | #include "Real.h" 2 | #include 3 | 4 | namespace DDG 5 | { 6 | Real :: Real( double x ) 7 | // constructs real number with value x 8 | : value( x ) 9 | {} 10 | 11 | Real :: operator double( void ) const 12 | // type cast to double 13 | { 14 | return value; 15 | } 16 | 17 | void Real :: operator+=( double x ) 18 | // increment 19 | { 20 | value += x; 21 | } 22 | 23 | void Real :: operator-=( double x ) 24 | // decrement 25 | { 26 | value -= x; 27 | } 28 | 29 | void Real :: operator*=( double x ) 30 | // multiply 31 | { 32 | value *= x; 33 | } 34 | 35 | void Real :: operator/=( double x ) 36 | // divide 37 | { 38 | value /= x; 39 | } 40 | 41 | Real Real :: conj( void ) const 42 | // simply returns the value (for compatibility w/ complex numbers) 43 | { 44 | return value; 45 | } 46 | 47 | Real Real :: inv( void ) const 48 | // returns inverse 49 | { 50 | return 1. / value; 51 | } 52 | 53 | double Real :: norm( void ) const 54 | // returns norm 55 | { 56 | return fabs( value ); 57 | } 58 | 59 | double Real :: norm2( void ) const 60 | // returns norm squared 61 | { 62 | return value * value; 63 | } 64 | 65 | Real Real :: unit( void ) const 66 | // returns number with unit norm and same sign 67 | { 68 | return value / norm(); 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /include/Edge.h: -------------------------------------------------------------------------------- 1 | // -*- Mode: c++ -*- 2 | // ----------------------------------------------------------------------------- 3 | // libDDG -- Edge.h 4 | // ----------------------------------------------------------------------------- 5 | // 6 | // Edge stores attributes associated with a mesh edge. The iterator he points 7 | // to one of its two associated halfedges. (See the documentation for a more 8 | // in-depth discussion of the halfedge data structure.) 9 | // 10 | 11 | #ifndef DDG_EDGE_H 12 | #define DDG_EDGE_H 13 | 14 | #include "Types.h" 15 | #include "Quaternion.h" 16 | #include "Complex.h" 17 | 18 | namespace DDG 19 | { 20 | class Edge 21 | { 22 | public: 23 | // points to one of the two halfedges associated with this edge 24 | HalfEdgeIter he; 25 | 26 | // distinguished direction at this edge 27 | // PS: only needed for old code 28 | const HalfEdgeIter X( void ) const { return he; } 29 | const Vector Xvector( void ) const; 30 | 31 | // 1/2 sum of incident triangle cotans 32 | const double cot( void ) const; 33 | 34 | // transport along edge with orientation he 35 | double r; 36 | // additional \omega rotation form \tilde{\nabla} = \nabla - J\omega 37 | double w; // REMOVE w 38 | // mass matrix coefficient 39 | Complex m; 40 | // energy matrix coefficient 41 | Complex Es; 42 | // for Hopf alignment with orientation of he 43 | Complex q; 44 | }; 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /include/Utility.h: -------------------------------------------------------------------------------- 1 | #ifndef DDG_UTILITY_H 2 | #define DDG_UTILITY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Complex.h" 10 | 11 | namespace DDGConstants 12 | { 13 | static DDG::Complex ii( 0., 1. ); 14 | const double PI = 3.14159265358979323846; 15 | } 16 | 17 | namespace DDG 18 | { 19 | inline double sqr( double x ) 20 | { 21 | return x*x; 22 | } 23 | 24 | inline double unitRand( void ) 25 | { 26 | const double rRandMax = 1. / (double) RAND_MAX; 27 | 28 | return rRandMax * (double) rand(); 29 | } 30 | 31 | inline double seconds( int t0, int t1 ) 32 | { 33 | return (double)(t1-t0) / (double) CLOCKS_PER_SEC; 34 | } 35 | 36 | inline const double fmodPI( const double a ) 37 | { 38 | using namespace DDGConstants; 39 | return a - (2*PI) * floor( (a+PI) / (2*PI) ); 40 | } 41 | 42 | inline double wallClock( void ) 43 | { 44 | struct timeval t; 45 | struct timezone tz; 46 | gettimeofday( &t, &tz ); 47 | return (double) t.tv_sec + 1e-6*(double) t.tv_usec; 48 | } 49 | 50 | inline void printTiming( const char* message, double t ) 51 | { 52 | using namespace std; 53 | 54 | void* callstack[128]; 55 | int stackDepth = backtrace( callstack, 128 ); 56 | 57 | for( int i = 0; i < stackDepth; i++ ) 58 | { 59 | cerr << " "; 60 | } 61 | cerr << "Wall clock time to " << message << ": " << t << " seconds." << endl; 62 | } 63 | } 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /include/Image.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // libDDG -- Image.h 3 | // ----------------------------------------------------------------------------- 4 | // 5 | // Image represents a color bitmap image. A simple example might look like 6 | // 7 | // Image im; 8 | // im.read( "input.tga" ); 9 | // // modify image data via im(x,y) = ...; 10 | // im.write( "output.tga" ); 11 | // 12 | 13 | #ifndef DDG_IMAGE_H 14 | #define DDG_IMAGE_H 15 | 16 | #include 17 | #include 18 | 19 | namespace DDG 20 | { 21 | class Image 22 | { 23 | public: 24 | Image( int width = 0, int height = 0 ); 25 | // constructs image with specified width and height 26 | 27 | float& operator()( int x, int y ); 28 | const float& operator()( int x, int y ) const; 29 | // accesses pixel (x,y) 30 | 31 | float sample( float x, float y ) const; 32 | // samples image at (x,y) using bilinear filtering 33 | 34 | int width( void ) const; 35 | int height( void ) const; 36 | // returns image dimensions 37 | 38 | void read( const char* filename ); 39 | // loads an image file in Truevision TGA format 40 | // (must be RGB image with 24 or 32 bits per pixel) 41 | 42 | void write( const char* filename ) const; 43 | // writes an image file in Truevision TGA format 44 | // (RGB image with 24 bits per pixel) 45 | 46 | protected: 47 | void clamp( int& x, int& y ) const; 48 | // clamps coordinates to range [0,w-1] x [0,h-1] 49 | 50 | int w, h; 51 | // width and height 52 | 53 | std::vector pixels; 54 | // interleaved RGBA pixel data in range [0-1] 55 | }; 56 | } 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/AliasTable.h: -------------------------------------------------------------------------------- 1 | #ifndef ALIASTABLE_H 2 | #define ALIASTABLE_H 3 | 4 | #include 5 | #include 6 | 7 | class AliasTable : public std::vector 8 | { 9 | public: 10 | // INTERFACE: 11 | // void build( void ); 12 | // int sample( void ) const; 13 | 14 | // IMPLEMENTATION: 15 | void build( void ) 16 | { 17 | const std::vector& value( *this ); 18 | std::stack rich, poor; 19 | double mean = 0.; 20 | 21 | for( size_t i = 0; i < size(); i++ ) 22 | { 23 | mean += value[i]; 24 | } 25 | mean /= (double) size(); 26 | 27 | alias.resize( size() ); 28 | percent.resize( size() ); 29 | 30 | for( size_t i = 0; i < size(); i++ ) 31 | { 32 | alias[i] = i; 33 | percent[i] = 1e-8 + value[i] / mean; 34 | if( percent[i] < 1. ) 35 | { 36 | poor.push( i ); 37 | } 38 | else 39 | { 40 | rich.push( i ); 41 | } 42 | } 43 | 44 | while( !poor.empty() ) 45 | { 46 | int p = poor.top(); poor.pop(); 47 | int r = rich.top(); 48 | 49 | percent[r] -= ( 1. - percent[p] ); 50 | alias[p] = r; 51 | 52 | if( percent[r] < 1. ) 53 | { 54 | rich.pop(); 55 | poor.push( r ); 56 | } 57 | } 58 | } 59 | 60 | int sample( void ) const 61 | { 62 | int i = rand() % size(); 63 | 64 | if( unitRand() < percent[i] ) 65 | { 66 | return i; 67 | } 68 | 69 | return alias[i]; 70 | } 71 | 72 | protected: 73 | double unitRand( void ) const 74 | { 75 | const double rRandMax = 1. / (double) RAND_MAX; 76 | 77 | return (double) rand() * rRandMax; 78 | } 79 | 80 | std::vector alias; 81 | std::vector percent; 82 | }; 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /include/Shader.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // libDDG -- Shader.h 3 | // ----------------------------------------------------------------------------- 4 | // 5 | // Shader encapsulates the functionality of a shader program written in 6 | // the OpenGL Shader Language (GLSL). Basic usage is to read a collection 7 | // of source files to disk and enable the shader before making draw calls. 8 | // For instance, during initialization one might write 9 | // 10 | // Shader shader; 11 | // shader.loadVertex( "vertex.glsl" ); 12 | // shader.loadFragment( "fragment.glsl" ); 13 | // 14 | // and in the main draw routine write 15 | // 16 | // shader.enable(); 17 | // // draw some stuff 18 | // shader.disable(); 19 | // 20 | 21 | #ifndef DDG_SHADER_H 22 | #define DDG_SHADER_H 23 | 24 | #include 25 | #include 26 | 27 | namespace DDG 28 | { 29 | class Shader 30 | { 31 | public: 32 | Shader( void ); 33 | // constructor -- shader is initially invalid 34 | 35 | ~Shader( void ); 36 | // destructor 37 | 38 | void loadVertex( const char* filename ); 39 | // read vertex shader from GLSL source file 40 | 41 | void loadFragment( const char* filename ); 42 | // read fragment shader from GLSL source file 43 | 44 | void loadGeometry( const char* filename ); 45 | // read geometry shader from GLSL source file 46 | 47 | void enable( void ); 48 | // uses this shader for rendering 49 | 50 | void disable( void ) const; 51 | // uses the fixed-function pipeline for rendering 52 | 53 | operator GLuint( void ) const; 54 | // returns the ID of this shader program (for calls to OpenGL) 55 | 56 | protected: 57 | void load( GLenum shaderType, const char* filename, GLuint& shader ); 58 | bool readSource( const char* filename, std::string& source ); 59 | 60 | GLuint vertexShader; 61 | GLuint fragmentShader; 62 | GLuint geometryShader; 63 | GLuint program; 64 | bool linked; 65 | }; 66 | } 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/Camera.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // libDDG -- Camera.h 3 | // ----------------------------------------------------------------------------- 4 | // 5 | // Camera is used by Viewer to keep track of the view state; it also 6 | // handles mouse input related to camera manipulation. 7 | // 8 | 9 | #ifndef DDG_CAMERA_H 10 | #define DDG_CAMERA_H 11 | 12 | #include "Quaternion.h" 13 | 14 | #ifdef __CYGWIN__ 15 | #define GLUT_DISABLE_ATEXIT_HACK 16 | #include 17 | #include 18 | #include 19 | #include 20 | #else 21 | #include 22 | #endif 23 | 24 | namespace DDG 25 | { 26 | class Camera 27 | { 28 | public: 29 | Camera( void ); 30 | // constructor 31 | 32 | Quaternion clickToSphere( int x, int y ); 33 | // projects a mous click onto the unit sphere 34 | 35 | void setView( void ) const; 36 | // applies the camera transformation to the OpenGL modelview stack 37 | 38 | void mouse( int button, int state, int x, int y ); 39 | // handles mouse clicks 40 | 41 | void motion( int x, int y ); 42 | // handles mouse drags 43 | 44 | void idle( void ); 45 | // handles camera momentum 46 | 47 | void zoomIn( void ); 48 | // moves viewer toward object 49 | 50 | void zoomOut( void ); 51 | // moves viewer away from object 52 | 53 | Quaternion currentRotation( void ) const; 54 | // returns the rotation corresponding to the current mouse state 55 | 56 | Quaternion pClick; 57 | // mouse coordinates of current click 58 | 59 | Quaternion pDrag; 60 | // mouse coordinates of current drag 61 | 62 | Quaternion pLast; 63 | // mouse coordinates of previous drag 64 | 65 | Quaternion rLast; 66 | // previous camera rotation 67 | 68 | Quaternion momentum; 69 | // camera momentum 70 | 71 | int tLast; 72 | // time of previous drag 73 | 74 | double zoom, vZoom; 75 | // zoom and zoom velocity 76 | }; 77 | } 78 | 79 | #endif 80 | 81 | -------------------------------------------------------------------------------- /include/Types.h: -------------------------------------------------------------------------------- 1 | // -*- Mode: c++ -*- 2 | // ----------------------------------------------------------------------------- 3 | // libDDG -- Types.h 4 | // ----------------------------------------------------------------------------- 5 | // 6 | // This file contains forward declarations of common types and definitions of 7 | // convenience types for standard iterators. 8 | // 9 | 10 | #ifndef DDG_TYPES_H 11 | #define DDG_TYPES_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace DDG 18 | { 19 | // forward declarations 20 | class Camera; 21 | class Complex; 22 | class Edge; 23 | class Face; 24 | class HalfEdge; 25 | class Image; 26 | class LinearContext; 27 | class LinearEquation; 28 | class LinearPolynomial; 29 | class LinearSystem; 30 | class Mesh; 31 | class MeshIO; 32 | class Quaternion; 33 | class Real; 34 | class Shader; 35 | class Variable; 36 | class Vector; 37 | class Vertex; 38 | class Viewer; 39 | 40 | template 41 | class DenseMatrix; 42 | 43 | template 44 | class SparseMatrix; 45 | 46 | // convenience types for iterators 47 | typedef std::map::iterator TermIter; 48 | typedef std::map::const_iterator TermCIter; 49 | typedef std::vector::iterator PolyIter; 50 | typedef std::vector::const_iterator PolyCIter; 51 | typedef std::vector::iterator EqnIter; 52 | typedef std::vector::const_iterator EqnCIter; 53 | typedef std::map::iterator IndexIter; 54 | typedef std::map::const_iterator IndexCIter; 55 | typedef std::vector::iterator HalfEdgeIter; 56 | typedef std::vector::const_iterator HalfEdgeCIter; 57 | typedef std::vector::iterator VertexIter; 58 | typedef std::vector::const_iterator VertexCIter; 59 | typedef std::vector::iterator EdgeIter; 60 | typedef std::vector::const_iterator EdgeCIter; 61 | typedef std::vector::iterator FaceIter; 62 | typedef std::vector::const_iterator FaceCIter; 63 | } 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /include/Complex.h: -------------------------------------------------------------------------------- 1 | #ifndef DDG_COMPLEX_H 2 | #define DDG_COMPLEX_H 3 | 4 | #include 5 | 6 | namespace DDG 7 | { 8 | class Complex 9 | { 10 | public: 11 | Complex( double a=0., double b=0. ); 12 | // constructs number a+bi 13 | 14 | void operator+=( const Complex& z ); 15 | // add z 16 | 17 | void operator-=( const Complex& z ); 18 | // subtract z 19 | 20 | void operator*=( const Complex& z ); 21 | // Complex multiply by z 22 | 23 | void operator*=( double r ); 24 | // scalar multiply by r 25 | 26 | void operator/=( double r ); 27 | // scalar divide by r 28 | 29 | void operator/=( const Complex& z ); 30 | // complex divide by r 31 | 32 | Complex operator-( void ) const; 33 | // returns the additive inverse 34 | 35 | Complex conj( void ) const; 36 | // returns Complex conjugate 37 | 38 | Complex inv( void ) const; 39 | // returns inverse 40 | 41 | double arg( void ) const; 42 | // returns argument 43 | 44 | double norm( void ) const; 45 | // returns norm 46 | 47 | double norm2( void ) const; 48 | // returns norm squared 49 | 50 | Complex unit( void ) const; 51 | // returns complex number with unit norm and same modulus 52 | 53 | double re; 54 | // real part 55 | 56 | double im; 57 | // imaginary part 58 | }; 59 | 60 | Complex operator+( const Complex& z1, const Complex& z2 ); 61 | // binary addition 62 | 63 | Complex operator-( const Complex& z1, const Complex& z2 ); 64 | // binary subtraction 65 | 66 | Complex operator*( const Complex& z1, const Complex& z2 ); 67 | // binary Complex multiplication 68 | 69 | Complex operator*( const Complex& z, double r ); 70 | // right scalar multiplication 71 | 72 | Complex operator*( double r, const Complex& z ); 73 | // left scalar multiplication 74 | 75 | Complex operator/( const Complex& z, double r ); 76 | // scalar division 77 | 78 | Complex operator/( const Complex& z1, const Complex& z2 ); 79 | // complex division 80 | 81 | double dot( const Complex& z1, const Complex& z2 ); 82 | // inner product 83 | 84 | double cross( const Complex& z1, const Complex& z2 ); 85 | // cross product 86 | 87 | Complex exp( const Complex& z ); 88 | // returns e^z 89 | 90 | std::ostream& operator<<( std::ostream& os, const Complex& o ); 91 | // prints components 92 | } 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/HalfEdge.cpp: -------------------------------------------------------------------------------- 1 | #include "HalfEdge.h" 2 | #include "Edge.h" 3 | #include "Mesh.h" 4 | #include "Vertex.h" 5 | #include "Vector.h" 6 | #include 7 | #include 8 | 9 | namespace DDG 10 | { 11 | // transport angle along this HalfEdge 12 | double HalfEdge::r( void ) const{ 13 | // if this halfedge is the one that the parent edge points to the 14 | // orientation is correct 15 | return edge->he == flip ? -edge->r : edge->r; 16 | } 17 | 18 | // additional rotation form // REMOVE w 19 | double HalfEdge::w( void ) const{ 20 | // if this halfedge is the one that the parent edge points to the 21 | // orientation is correct 22 | return edge->he == flip ? -edge->w : edge->w; 23 | } 24 | 25 | // orientation of this halfedge with respec to parent edge 26 | int HalfEdge::sign( void ) const{ 27 | return edge->he == flip ? -1 : 1; 28 | } 29 | 30 | // mass matrix coefficient along this edge 31 | Complex HalfEdge::m( void ) const{ 32 | // if this halfedge is the one that the parent edge points to the 33 | // orientation is correct 34 | return edge->he == flip ? edge->m.conj() : edge->m; 35 | } 36 | 37 | // accumulator function for mass matrix coefficient along this edge 38 | void HalfEdge::mAcc( const Complex& m ){ 39 | // if this halfedge is the one that the parent edge points to the 40 | // orientation is correct 41 | if( edge->he == flip ) edge->m += m.conj(); else edge->m += m; 42 | } 43 | 44 | // energy matrix coefficient along this edge 45 | Complex HalfEdge::Es( void ) const{ 46 | // if this halfedge is the one that the parent edge points to the 47 | // orientation is correct 48 | return edge->he == flip ? edge->Es.conj() : edge->Es; 49 | } 50 | 51 | // accumulator function for Energy matrix coefficient along this edge 52 | void HalfEdge::EsAcc( const Complex& Es ){ 53 | // if this halfedge is the one that the parent edge points to the 54 | // orientation is correct 55 | if( edge->he == flip ) edge->Es += Es.conj(); else edge->Es += Es; 56 | } 57 | 58 | Vector HalfEdge::geom( void ) const{ 59 | return flip->vertex->position - vertex->position; 60 | } 61 | 62 | const double HalfEdge::cot( void ) const{ 63 | // assumes triangles 64 | assert( face->he->next->next->next == face->he ); 65 | 66 | const Vector pki = next->geom(), pij = next->next->geom(); 67 | // /|pij \times pik| 68 | return -dot(pij,pki)/cross(pki,pij).norm(); 69 | } 70 | 71 | const double HalfEdge::cornerAngle( void ) const 72 | { 73 | Vector pi = vertex->position; 74 | Vector pj = next->vertex->position; 75 | Vector pk = next->next->vertex->position; 76 | Vector u = ( pj-pi ).unit(); 77 | Vector v = ( pk-pi ).unit(); 78 | return acos( dot( u, v )); 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /include/HalfEdge.h: -------------------------------------------------------------------------------- 1 | // -*- Mode: c++ -*- 2 | // ----------------------------------------------------------------------------- 3 | // libDDG -- HalfEdge.h 4 | // ----------------------------------------------------------------------------- 5 | // 6 | // HalfEdge is used to define mesh connectivity. (See the documentation for a 7 | // more in-depth discussion of the halfedge data structure.) 8 | // 9 | 10 | #ifndef DDG_HALFEDGE_H 11 | #define DDG_HALFEDGE_H 12 | 13 | #include "Vector.h" 14 | #include "Types.h" 15 | #include "Complex.h" 16 | #include "Quaternion.h" 17 | 18 | namespace DDG 19 | { 20 | class HalfEdge 21 | { 22 | public: 23 | HalfEdgeIter next; 24 | // points to the next halfedge around the current face 25 | 26 | HalfEdgeIter flip; 27 | // points to the other halfedge associated with this edge 28 | 29 | VertexIter vertex; 30 | // points to the vertex at the "tail" of this halfedge 31 | 32 | EdgeIter edge; 33 | // points to the edge associated with this halfedge 34 | 35 | FaceIter face; 36 | // points to the face containing this halfedge 37 | 38 | bool onBoundary; 39 | // true if this halfedge is contained in a boundary 40 | // loop; false otherwise 41 | 42 | Vector texcoord; 43 | // texture coordinates associated with the triangle corner at the 44 | // "tail" of this halfedge 45 | 46 | // retrieves transport angle along this HalfEdge from parent 47 | // Edge depending on orientation 48 | double r( void ) const; 49 | 50 | // if we work with a changed connection \tilde{\nabla} = \nabla - Jw 51 | // we find the rotation form here 52 | double w( void ) const; // REMOVE w 53 | 54 | // sometimes we need to know the orientation of this halfedge with 55 | // respect to its parent edge 56 | int sign( void ) const; 57 | 58 | // retrieves mass matrix coefficient along this HalfEdge from 59 | // parent Edge depending on orientation 60 | Complex m( void ) const; 61 | 62 | // retrieves Energy matrix coefficient along this HalfEdge from 63 | // parent Edge depending on orientation 64 | Complex Es( void ) const; 65 | 66 | // accumulator function for mass matrix coefficient along this 67 | // HalfEdge updating parent Edge depending on orientation 68 | void mAcc( const Complex& m ); 69 | 70 | // accumulator function for Energy matrix coefficient along this 71 | // HalfEdge updating parent Edge depending on orientation 72 | void EsAcc( const Complex& Es ); 73 | 74 | // embedded geometry of this edge 75 | Vector geom( void ) const; 76 | 77 | // good old fashioned cotan of angle across from this halfedge 78 | const double cot( void ) const; 79 | 80 | // angle of triangle corner at the "tail" of this halfedge 81 | const double cornerAngle( void ) const; 82 | }; 83 | } 84 | 85 | #endif 86 | 87 | -------------------------------------------------------------------------------- /include/MeshIO.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // libDDG -- MeshIO.h 3 | // ----------------------------------------------------------------------------- 4 | // 5 | // MeshIO handles input/output operations for Mesh objects. Currently the only 6 | // supported mesh format is Wavefront OBJ -- for a format specification see 7 | // 8 | // http://en.wikipedia.org/wiki/Wavefront_.obj_file 9 | // 10 | // Note that vertex normals and material properties are currently ignored. 11 | // 12 | 13 | #ifndef DDG_MESHIO_H 14 | #define DDG_MESHIO_H 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "Vector.h" 22 | 23 | namespace DDG 24 | { 25 | class Mesh; 26 | class Index; 27 | class MeshData; 28 | 29 | class MeshIO 30 | { 31 | public: 32 | static int read( std::istream& in, Mesh& mesh ); 33 | // reads a mesh from a valid, open input stream in 34 | 35 | static void write( std::ostream& out, const Mesh& mesh, unsigned int n ); 36 | // writes a mesh to a valid, open output stream out 37 | 38 | static int buildMesh( const MeshData& data, Mesh& mesh ); 39 | // made visible to help interface with Houdini 40 | protected: 41 | static int readMeshData( std::istream& in, MeshData& data ); 42 | static void readPosition( std::stringstream& ss, MeshData& data ); 43 | static void readTexCoord( std::stringstream& ss, MeshData& data ); 44 | static void readNormal ( std::stringstream& ss, MeshData& data ); 45 | static void readFace ( std::stringstream& ss, MeshData& data ); 46 | static Index parseFaceIndex( const std::string& token ); 47 | static void preallocateMeshElements( const MeshData& data, Mesh& mesh ); 48 | static void checkIsolatedVertices( const Mesh& Mesh ); 49 | static void checkNonManifoldVertices( const Mesh& Mesh ); 50 | }; 51 | 52 | class Index 53 | { 54 | public: 55 | Index( void ) 56 | {} 57 | 58 | Index( int p, int t, int n ) 59 | : position( p ), texcoord( t ), normal( n ) 60 | {} 61 | 62 | bool operator<( const Index& i ) const 63 | { 64 | if( position < i.position ) return true; 65 | if( position > i.position ) return false; 66 | if( texcoord < i.texcoord ) return true; 67 | if( texcoord > i.texcoord ) return false; 68 | if( normal < i.normal ) return true; 69 | if( normal > i.normal ) return false; 70 | return false; 71 | } 72 | 73 | int position; 74 | int texcoord; 75 | int normal; 76 | }; 77 | 78 | class MeshData 79 | { 80 | public: 81 | std::vector positions; 82 | std::vector texcoords; 83 | std::vector normals; 84 | std::vector< std::vector< Index > > indices; 85 | }; 86 | } 87 | 88 | #endif 89 | 90 | -------------------------------------------------------------------------------- /include/Face.h: -------------------------------------------------------------------------------- 1 | // -*- Mode: c++ -*- 2 | // ----------------------------------------------------------------------------- 3 | // libDDG -- Face.h 4 | // ----------------------------------------------------------------------------- 5 | // 6 | // Face stores attributes associated with a mesh edge. The iterator he points 7 | // to one of its associated halfedges. (See the documentation for a more 8 | // in-depth discussion of the halfedge data structure.) 9 | // 10 | 11 | #ifndef DDG_FACE_H 12 | #define DDG_FACE_H 13 | 14 | #include "Types.h" 15 | #include "Complex.h" 16 | #include "Vector.h" 17 | 18 | namespace DDG 19 | { 20 | class Face 21 | { 22 | public: 23 | HalfEdgeIter he; 24 | // points to one of the halfedges associated with this face 25 | HalfEdgeCIter parent; 26 | // needed for generator construction (should not be here...) 27 | 28 | // PS: For a face the predicate is a function and called isBoundary 29 | // PS: for halfedges it is a member and called onBoundary 30 | // PS: I think making this consistent would be desirable 31 | bool isBoundary( void ) const; 32 | // returns true if this face corresponds to a 33 | // boundary loop; false otherwise 34 | 35 | // distinguished direction 36 | const HalfEdgeIter X( void ) const { return he; } 37 | const Vector Xvector( void ) const; 38 | 39 | // which is v? 0,1,2? 40 | unsigned int i( const VertexCIter v ) const; 41 | 42 | // field 43 | Complex u; 44 | 45 | // area of triangle 46 | double A; 47 | 48 | // corner angles 49 | double alpha[3]; 50 | 51 | // index of this triangle 52 | int sing; 53 | 54 | Vector normal; 55 | void updateNormal( void ); 56 | // returns the unit normal associated with this face; normal 57 | // orientation is determined by the circulation order of halfedges 58 | 59 | Vector Anormal; 60 | void updateANormal( void ); 61 | // returns the area weighted normal 62 | 63 | double area( void ) const; 64 | // returns the triangle area 65 | 66 | Vector barycenter( void ) const; 67 | // returns the mean of vertex coordinates 68 | 69 | // as the name says 70 | void InitAreaAnglesAngleSums( void ); 71 | 72 | // geometric curvature 73 | double K( void ) const; 74 | 75 | // PS: Wishlist 76 | // PS: unsigned int valence( void ) const; (or degree() if you wish) 77 | // PS: an assertion if things are not triangle in all the places that assume it 78 | 79 | // visualization --------------------------------------------------- 80 | Vector sampleUniform( void ) const; 81 | // samples a point uniformly at random, expressed in barycentric 82 | // coordinates relative to he->vertex, he->next->vertex, and 83 | // he->next->next->vertex, respectively. 84 | 85 | Vector sampleField( const Vector& p, double n ) const; 86 | // samples the interpolated field u at the point p expressed in 87 | // barycentric coordinates relative to he->vertex, he->next->vertex, and 88 | // he->next->next->vertex, respectively. 89 | // must specify the field degree n 90 | 91 | // converts from barycentric coordinates to (x,y,z) coords in R^3 92 | Vector toWorldCoordinates( const Vector& barycentric ) const; 93 | }; 94 | } 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | #include "Camera.h" 7 | 8 | namespace DDG 9 | { 10 | Camera :: Camera( void ) 11 | : pClick( 1. ), 12 | pDrag( 1. ), 13 | pLast( 1. ), 14 | rLast( 1. ), 15 | momentum( 1. ), 16 | zoom( 1. ) 17 | {} 18 | 19 | Quaternion Camera :: clickToSphere( int x, int y ) 20 | { 21 | GLint viewport[4]; 22 | glGetIntegerv( GL_VIEWPORT, viewport ); 23 | int w = viewport[2]; 24 | int h = viewport[3]; 25 | 26 | Quaternion p( 0., 27 | 2. * (double) x / (double) w - 1., 28 | 2. * (double) y / (double) h - 1., 29 | 0. ); 30 | 31 | if( p.norm2() > 1. ) 32 | { 33 | p.normalize(); 34 | p.im().z = 0.; 35 | } 36 | else 37 | { 38 | p.im().z = sqrt( 1. - p.norm2() ); 39 | } 40 | 41 | return p; 42 | } 43 | 44 | Quaternion Camera :: currentRotation( void ) const 45 | { 46 | return ( pDrag * pClick.conj() ) * rLast; 47 | } 48 | 49 | void Camera :: setView( void ) const 50 | { 51 | Quaternion r = ( pDrag * pClick.conj() ) * rLast; 52 | 53 | double w = r[0]; 54 | double x = r[1]; 55 | double y = r[2]; 56 | double z = r[3]; 57 | 58 | GLdouble M[16] = { 59 | 1.-2.*y*y-2.*z*z, 2.*x*y+2.*w*z, 2.*x*z-2.*w*y, 0., 60 | 2.*x*y-2.*w*z, 1.-2.*x*x-2.*z*z, 2.*y*z+2.*w*x, 0., 61 | 2.*x*z+2.*w*y, 2.*y*z-2.*w*x, 1.-2.*x*x-2.*y*y, 0., 62 | 0., 0., 0., 1. 63 | }; 64 | 65 | glMatrixMode( GL_MODELVIEW ); 66 | glMultMatrixd( M ); 67 | } 68 | 69 | void Camera :: mouse( int button, int state, int x, int y ) 70 | { 71 | if( state == GLUT_DOWN ) 72 | { 73 | pClick = pDrag = pLast = clickToSphere( x, y ); 74 | momentum = 1.; 75 | } 76 | if( state == GLUT_UP ) 77 | { 78 | double timeSinceDrag = ( clock() - tLast ) / (double) CLOCKS_PER_SEC; 79 | 80 | if( timeSinceDrag < .1 ) 81 | { 82 | momentum = pDrag * pLast.conj(); 83 | momentum = ( .03 * momentum + .97 ).unit(); 84 | } 85 | else 86 | { 87 | momentum = 1.; 88 | } 89 | 90 | rLast = pDrag * pClick.conj() * rLast; 91 | pClick = pDrag = 1.; 92 | } 93 | } 94 | 95 | void Camera :: motion( int x, int y ) 96 | { 97 | tLast = clock(); 98 | pLast = pDrag; 99 | pDrag = clickToSphere( x, y ); 100 | } 101 | 102 | void Camera :: idle( void ) 103 | { 104 | // get time since last idle event 105 | static int t0 = clock(); 106 | int t1 = clock(); 107 | double dt = (t1-t0) / (double) CLOCKS_PER_SEC; 108 | 109 | rLast = momentum * rLast; 110 | momentum = ( (1.-.5*dt) * momentum + .5*dt ).unit(); 111 | 112 | zoom += vZoom*dt; 113 | vZoom *= max( 0., 1.-5.*dt ); 114 | 115 | t0 = t1; 116 | } 117 | 118 | void Camera :: zoomIn( void ) 119 | { 120 | vZoom -= 0.5; 121 | } 122 | 123 | void Camera :: zoomOut( void ) 124 | { 125 | vZoom += 0.5; 126 | } 127 | } 128 | 129 | -------------------------------------------------------------------------------- /src/Vertex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | #include "Vertex.h" 7 | #include "Mesh.h" 8 | #include "HalfEdge.h" 9 | #include "Quaternion.h" 10 | 11 | namespace DDG 12 | { 13 | // returns the vertex normal 14 | void Vertex::updateNormal( void ) { 15 | Vector N(0,0,0); 16 | 17 | HalfEdgeCIter h = he; 18 | // PS: bugfix 19 | do{ if( !h->face->isBoundary() ) N += h->face->Anormal; }while( ( h = h->flip->next ) != he ); 20 | 21 | // returns the volume gradient normal 22 | normal = N.unit(); 23 | } 24 | 25 | vector isolated; // all isolated vertices point to isolated.begin() 26 | 27 | // returns true if the vertex is not contained in any face or edge; false otherwise 28 | bool Vertex::isIsolated( void ) const { return he == isolated.begin(); } 29 | 30 | // returns the number of incident faces 31 | const unsigned int Vertex :: valence( void ) const { 32 | unsigned int n = 0; 33 | 34 | HalfEdgeCIter h = he; 35 | do{ n++; }while( ( h = h->flip->next ) != he ); 36 | 37 | return n; 38 | } 39 | 40 | // return the kVec as a worldspace vector 41 | const Vector Vertex::kVec( void ) const { 42 | const Vector x = Xvector(); 43 | const Vector n = normal; 44 | // the corresponding tangent vector 45 | const Vector xt = ( x - dot(x,n)*n ).unit(); 46 | const double theta = u.arg(); 47 | const Quaternion r(cos(theta/2),sin(theta/2)*n); 48 | 49 | return u.norm()*(r*Quaternion(0,xt)*r.conj()).im(); 50 | } 51 | 52 | const Vector Vertex::Xvector( void ) const{ 53 | return X()->next->vertex->position - X()->vertex->position; 54 | } 55 | 56 | double 57 | Vertex::AngleOfEdge( const HalfEdgeCIter he ) const{ 58 | // measure the angle from X() to he 59 | HalfEdgeIter hi = X(); double aCi = 0; 60 | while( hi != he ){ 61 | // no angle to add for outer face; notice that we exit. The 62 | // assumption is that for a vertex on the boundary the 63 | // (only!) outer face will be the last face; so just exit 64 | if( hi->face->isBoundary() ) break; 65 | 66 | // get the angle that needs to be added to get to the next edge 67 | aCi += hi->face->alpha[hi->face->i(this->he->vertex)]; 68 | 69 | // iterate 70 | hi = hi->next->next->flip; 71 | 72 | // guard against infinite loop 73 | assert( hi != X() ); 74 | } 75 | return aCi * s; 76 | } 77 | 78 | void 79 | Vertex::EnforceBoundaryHalfEdgeConvention( void ){ 80 | // if a vertex is on the boundary we want its he pointer to point 81 | // to the first boundary edge in CCW order; enforce this here 82 | HalfEdgeIter hi = he; 83 | do{ 84 | // if there is a boundary face bend the he pointer and exit 85 | if( hi->flip->face->isBoundary() ){ he = hi; break; } 86 | }while( ( hi = hi->flip->next ) != he ); 87 | } 88 | 89 | double Vertex::BoundaryNormalAngle() 90 | { 91 | HalfEdgeIter h = he; 92 | do 93 | { 94 | if( h->onBoundary ) break; 95 | h = h->next->next->flip; 96 | } 97 | while( h != he ); 98 | 99 | return AngleOfEdge( h )/2.; 100 | } 101 | 102 | Complex Vertex::BoundaryValue( unsigned int n ) 103 | { 104 | double theta = n * BoundaryNormalAngle(); 105 | return Complex( cos(theta), sin(theta) ); 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /src/commandline.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | #include "DenseMatrix.h" 6 | #include "Mesh.h" 7 | using namespace DDG; 8 | 9 | // Command line parsing code courtesy Rohan Sawhney! 10 | 11 | void printUsage(const std::string& programName) 12 | { 13 | std::cout << "usage: " 14 | << programName << " " 15 | << "OBJ_INPUT_PATH " 16 | << "OBJ_OUTPUT_PATH " 17 | << "[--degree=n] " 18 | << "[--alignToCurvature] " 19 | << "[--alignToBoundary] " 20 | << "[--s=S] " 21 | << "[--t=T]" 22 | << std::endl; 23 | } 24 | 25 | bool doesArgExist(const std::string& arg, const std::string& searchStr) 26 | { 27 | return arg.find(searchStr) != std::string::npos; 28 | } 29 | 30 | bool parseArg(const std::string& arg, const std::string& searchStr, std::string& value) 31 | { 32 | if (doesArgExist(arg, searchStr)) { 33 | value = arg.substr(arg.find_first_of(searchStr[searchStr.size()-1]) + 1); 34 | return true; 35 | } 36 | 37 | return false; 38 | } 39 | 40 | void parseArgs(int argc, char *argv[], std::string& inputPath, std::string& outputPath, 41 | int& degree, bool& alignToCurvature, bool& alignToBoundary, double& s, double& t) 42 | { 43 | if (argc < 3) { 44 | // input and/or output path not specified 45 | printUsage(argv[0]); 46 | exit(EXIT_FAILURE); 47 | 48 | } else { 49 | // parse arguments 50 | inputPath = argv[1]; 51 | outputPath = argv[2]; 52 | std::string degreeStr; 53 | std::string sStr, tStr; 54 | 55 | for (int i = 3; i < argc; i++) { 56 | if (parseArg(argv[i], "--degree=", degreeStr)) degree = std::stoi(degreeStr); 57 | if (doesArgExist(argv[i], "--alignToCurvature")) alignToCurvature = true; 58 | if (doesArgExist(argv[i], "--alignToBoundary")) alignToBoundary = true; 59 | if (parseArg(argv[i], "--s=", sStr)) s = std::atof(sStr.c_str()); 60 | if (parseArg(argv[i], "--t=", tStr)) t = std::atof(tStr.c_str()); 61 | } 62 | } 63 | 64 | // aligning to boundary takes precedence over aligning to curvature 65 | if (alignToBoundary) { 66 | alignToCurvature = false; 67 | } 68 | } 69 | 70 | int main( int argc, char** argv ) 71 | { 72 | // parse command line options 73 | std::string inputPath = ""; 74 | std::string outputPath = ""; 75 | int degree = 1; 76 | bool alignToCurvature = false; 77 | bool alignToBoundary = false; 78 | double s = 0.; 79 | double t = 0.; 80 | parseArgs( argc, argv, inputPath, outputPath, degree, alignToCurvature, alignToBoundary, s, t ); 81 | 82 | Mesh mesh; 83 | 84 | cout << "Reading mesh from " << inputPath << "..." << endl; 85 | mesh.read( inputPath ); 86 | 87 | cout << "Computing field..." << endl; 88 | mesh.InitKVecDirData(); 89 | mesh.clearSingularities(); 90 | if( alignToCurvature ) 91 | { 92 | mesh.SmoothestCurvatureAlignment( degree, s, t, true ); 93 | } 94 | else if( alignToBoundary ) 95 | { 96 | mesh.ComputeSmoothestFixedBoundary( degree, s, true ); 97 | } 98 | else 99 | { 100 | mesh.ComputeSmoothest( degree, s, true ); 101 | } 102 | 103 | cout << "Writing solution to " << outputPath << "..." << endl; 104 | mesh.write( outputPath, degree ); 105 | 106 | return 0; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /include/Vertex.h: -------------------------------------------------------------------------------- 1 | // -*- Mode: c++ -*- 2 | // ----------------------------------------------------------------------------- 3 | // libDDG -- Vertex.h 4 | // ----------------------------------------------------------------------------- 5 | // 6 | // Vertex stores attributes associated with a mesh edge. The iterator he 7 | // points to its "outgoing" halfedge. (See the documentation for a more 8 | // in-depth discussion of the halfedge data structure.) 9 | // 10 | 11 | #ifndef DDG_VERTEX_H 12 | #define DDG_VERTEX_H 13 | 14 | #include "Vector.h" 15 | #include "Types.h" 16 | #include "Complex.h" 17 | #include "HalfEdge.h" 18 | 19 | namespace DDG 20 | { 21 | class Vertex 22 | { 23 | public: 24 | HalfEdgeIter he; 25 | // points to the "outgoing" halfedge 26 | HalfEdgeCIter parent; 27 | // needed in generators construction (shouldn't be here...) 28 | 29 | Vector position; 30 | // location of vertex in Euclidean 3-space 31 | 32 | // PS: K-vec/dir stuff 33 | // coefficient of PL section 34 | Complex u; 35 | 36 | // basis direction, which is a half edge 37 | const HalfEdgeIter X( void ) const { return he; } 38 | 39 | // resp. an actual vector in world space 40 | const Vector Xvector( void ) const; 41 | 42 | // for display/output: one of n vectors representing the field (e.g., one of four "arms" of a cross) 43 | const Vector fieldVector( void ) const; 44 | 45 | // angle scale factor for intrinsic tangent spaces 46 | double s; 47 | 48 | // mass of this vertex (happens to be real, but otherwise is a 49 | // complex coefficient); also called m_{ii} in the paper 50 | double m; 51 | 52 | // energy matrix coefficient Es_{ii} 53 | double Es; 54 | 55 | // Coefficient of PL section used in alignemnt (initialized to 56 | // Hopf differential as that is the canonical use of this) 57 | Complex q; 58 | 59 | // returns the kVec in world space 60 | const Vector kVec( void ) const; 61 | 62 | Vector normal; 63 | void updateNormal( void ); 64 | // returns the vertex unit normal (volume gradient normal, 65 | // i.e., area weighted adding of incident face normals) 66 | 67 | bool isIsolated( void ) const; 68 | // returns true if the vertex is not contained in any face or edge; false otherwise 69 | 70 | // PS: counts number of edges, not faces (makes a difference at the boundary) 71 | const unsigned int valence( void ) const; 72 | // returns the number of incident faces 73 | 74 | double AngleOfEdge( const HalfEdgeCIter he ) const; 75 | // returns angle from X() direction to he in the flattened tangent space 76 | 77 | void EnforceBoundaryHalfEdgeConvention( void ); 78 | // some code is greatly simplified if we may assume that the he 79 | // pointer of a boundary vertex points to the first boundary edge 80 | // in CCW order; enforce this here 81 | 82 | // PS: gross. but this is gotta be good enough for now (amortized cost is constant) 83 | bool onBoundary( void ) const { 84 | HalfEdgeIter hi = he; 85 | do{ if( hi->onBoundary ) return true; }while( ( hi = hi->flip->next ) != he ); 86 | return false; 87 | } 88 | 89 | double BoundaryNormalAngle(); 90 | // for a boundary vertex, return angle corresponding to the 91 | // (inward) vector orthogonal to the domain boundary 92 | 93 | Complex BoundaryValue( unsigned int n ); 94 | // complex value corresponding to the boundary normal, for an n-direction field 95 | 96 | int id; 97 | // unique ID (used to index matrices) 98 | }; 99 | } 100 | 101 | #endif 102 | 103 | -------------------------------------------------------------------------------- /src/Vector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Vector.h" 4 | #include "Complex.h" 5 | 6 | namespace DDG 7 | { 8 | Vector :: Vector( void ) 9 | : x( 0. ), 10 | y( 0. ), 11 | z( 0. ) 12 | {} 13 | 14 | Vector :: Vector( double x0, 15 | double y0, 16 | double z0 ) 17 | : x( x0 ), 18 | y( y0 ), 19 | z( z0 ) 20 | {} 21 | 22 | Vector :: Vector( const Vector& v ) 23 | : x( v.x ), 24 | y( v.y ), 25 | z( v.z ) 26 | {} 27 | 28 | // Vector :: Vector( const Complex& z ) 29 | // : x( z.re ), 30 | // y( z.im ), 31 | // z( 0. ) 32 | // {} 33 | 34 | double& Vector :: operator[]( const int& index ) 35 | { 36 | return ( &x )[ index ]; 37 | } 38 | 39 | const double& Vector :: operator[]( const int& index ) const 40 | { 41 | return ( &x )[ index ]; 42 | } 43 | 44 | Vector Vector :: operator+( const Vector& v ) const 45 | { 46 | return Vector( x + v.x, 47 | y + v.y, 48 | z + v.z ); 49 | } 50 | 51 | Vector Vector :: operator-( const Vector& v ) const 52 | { 53 | return Vector( x - v.x, 54 | y - v.y, 55 | z - v.z ); 56 | } 57 | 58 | Vector Vector :: operator-( void ) const 59 | { 60 | return Vector( -x, 61 | -y, 62 | -z ); 63 | } 64 | 65 | Vector Vector :: operator*( const double& c ) const 66 | { 67 | return Vector( x*c, 68 | y*c, 69 | z*c ); 70 | } 71 | 72 | Vector operator*( const double& c, const Vector& v ) 73 | { 74 | return v*c; 75 | } 76 | 77 | Vector Vector :: operator/( const double& c ) const 78 | { 79 | return (*this) * ( 1./c ); 80 | } 81 | 82 | void Vector :: operator+=( const Vector& v ) 83 | { 84 | x += v.x; 85 | y += v.y; 86 | z += v.z; 87 | } 88 | 89 | void Vector :: operator-=( const Vector& v ) 90 | { 91 | x -= v.x; 92 | y -= v.y; 93 | z -= v.z; 94 | } 95 | 96 | void Vector :: operator*=( const double& c ) 97 | { 98 | x *= c; 99 | y *= c; 100 | z *= c; 101 | } 102 | 103 | void Vector :: operator/=( const double& c ) 104 | { 105 | (*this) *= ( 1./c ); 106 | } 107 | 108 | double Vector :: norm( void ) const 109 | { 110 | return sqrt( norm2()); 111 | } 112 | 113 | double Vector :: norm2( void ) const 114 | { 115 | return dot( *this, *this ); 116 | } 117 | 118 | void Vector :: normalize( void ) 119 | { 120 | (*this) /= norm(); 121 | } 122 | 123 | Vector Vector :: unit( void ) const 124 | { 125 | return (*this) / norm(); 126 | } 127 | 128 | Vector Vector :: abs( void ) const 129 | { 130 | return Vector( fabs( x ), 131 | fabs( y ), 132 | fabs( z ) ); 133 | } 134 | 135 | double dot( const Vector& u, const Vector& v ) 136 | { 137 | return u.x*v.x + 138 | u.y*v.y + 139 | u.z*v.z ; 140 | } 141 | 142 | Vector cross( const Vector& u, const Vector& v ) 143 | { 144 | return Vector( u.y*v.z - u.z*v.y, 145 | u.z*v.x - u.x*v.z, 146 | u.x*v.y - u.y*v.x ); 147 | } 148 | 149 | std::ostream& operator << (std::ostream& os, const Vector& o) 150 | { 151 | os << "[ " 152 | << o.x << " " 153 | << o.y << " " 154 | << o.z 155 | << " ]"; 156 | 157 | return os; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /include/Vector.h: -------------------------------------------------------------------------------- 1 | // -*- Mode: c++ -*- 2 | // ----------------------------------------------------------------------------- 3 | // libDDG -- Vector.h 4 | // ----------------------------------------------------------------------------- 5 | // 6 | // Vector represents an element of Euclidean 3-space, along with all the usual 7 | // vectors space operations (addition, multiplication by scalars, etc.). The 8 | // inner product (i.e., scalar or dot product) is expressed using the global 9 | // method dot(): 10 | // 11 | // Vector u, v; 12 | // double cosTheta = dot( u, v ); 13 | // 14 | // and the cross product is expressed using the global method cross(): 15 | // 16 | // Vector u, v, w; 17 | // w = cross( u, v ); 18 | // 19 | // Individual components can be accessed in two ways: either directly via the 20 | // members x, y, and z: 21 | // 22 | // Vector v; 23 | // cout << v.x << endl; 24 | // cout << v.y << endl; 25 | // cout << v.z << endl; 26 | // 27 | // or by index: 28 | // 29 | // Vector v; 30 | // for( int i = 0; i < 3; i++ ) 31 | // { 32 | // cout << v[i] << endl; 33 | // } 34 | // 35 | 36 | #ifndef DDG_VECTOR_H 37 | #define DDG_VECTOR_H 38 | 39 | #include 40 | 41 | namespace DDG 42 | { 43 | class Complex; 44 | 45 | class Vector 46 | { 47 | public: 48 | Vector(); 49 | // initializes all components to zero 50 | 51 | Vector( double x, double y, double z); 52 | // initializes with specified components 53 | 54 | Vector( const Vector& v ); 55 | // initializes from existing vector 56 | 57 | double& operator[] ( const int& index ); 58 | // returns reference to the specified component (0-based indexing: x, y, z) 59 | 60 | const double& operator[] ( const int& index ) const; 61 | // returns const reference to the specified component (0-based indexing: x, y, z) 62 | 63 | Vector operator+( const Vector& v ) const; 64 | // addition 65 | 66 | Vector operator-( const Vector& v ) const; 67 | // subtraction 68 | 69 | Vector operator-( void ) const; 70 | // negation 71 | 72 | Vector operator*( const double& c ) const; 73 | // right scalar multiplication 74 | 75 | Vector operator/( const double& c ) const; 76 | // scalar division 77 | 78 | void operator+=( const Vector& v ); 79 | // addition / assignment 80 | 81 | void operator-=( const Vector& v ); 82 | // subtraction / assignment 83 | 84 | void operator*=( const double& c ); 85 | // scalar multiplication / assignment 86 | 87 | void operator/=( const double& c ); 88 | // scalar division / assignment 89 | 90 | double norm( void ) const; 91 | // returns Euclidean length 92 | 93 | double norm2( void ) const; 94 | // returns Euclidean length squared 95 | 96 | Vector unit( void ) const; 97 | // returns unit vector 98 | 99 | void normalize( void ); 100 | // divides by Euclidean length 101 | 102 | Vector abs( void ) const; 103 | // returns vector containing magnitude of each component 104 | 105 | double x, y, z; 106 | // components 107 | }; 108 | 109 | Vector operator* ( const double& c, const Vector& v ); 110 | // left scalar multiplication 111 | 112 | double dot( const Vector& u, const Vector& v ); 113 | // dot product (a.k.a. inner or scalar product) 114 | 115 | Vector cross( const Vector& u, const Vector& v ); 116 | // cross product 117 | 118 | std::ostream& operator << (std::ostream& os, const Vector& o); 119 | // prints components 120 | } 121 | 122 | #endif 123 | 124 | -------------------------------------------------------------------------------- /src/Shader.cpp: -------------------------------------------------------------------------------- 1 | #include "Shader.h" 2 | 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | namespace DDG 8 | { 9 | Shader::Shader( void ) 10 | // constructor -- shader is initially invalid 11 | : vertexShader( 0 ), 12 | fragmentShader( 0 ), 13 | geometryShader( 0 ), 14 | program( 0 ), 15 | linked( false ) 16 | {} 17 | 18 | Shader::~Shader( void ) 19 | { 20 | if( program ) glDeleteProgram( program ); 21 | 22 | if( vertexShader ) glDeleteShader( vertexShader ); 23 | if( fragmentShader ) glDeleteShader( fragmentShader ); 24 | if( geometryShader ) glDeleteShader( geometryShader ); 25 | } 26 | 27 | void Shader::loadVertex( const char* filename ) 28 | { 29 | load( GL_VERTEX_SHADER, filename, vertexShader ); 30 | } 31 | 32 | void Shader::loadFragment( const char* filename ) 33 | { 34 | load( GL_FRAGMENT_SHADER, filename, fragmentShader ); 35 | } 36 | 37 | void Shader::loadGeometry( const char* filename ) 38 | { 39 | #ifdef GL_GEOMETRY_SHADER_EXT 40 | load( GL_GEOMETRY_SHADER_EXT, filename, geometryShader ); 41 | #else 42 | cerr << "Error: geometry shaders not supported!" << endl; 43 | #endif 44 | } 45 | 46 | void Shader::enable( void ) 47 | { 48 | if( !linked ) 49 | { 50 | glLinkProgram( program ); 51 | linked = true; 52 | } 53 | 54 | glUseProgram( program ); 55 | } 56 | 57 | void Shader::disable( void ) const 58 | { 59 | glUseProgram( 0 ); 60 | } 61 | 62 | Shader::operator GLuint( void ) const 63 | { 64 | return program; 65 | } 66 | 67 | void Shader::load( GLenum shaderType, const char* filename, GLuint& shader ) 68 | // read vertex shader from GLSL source file, compile, and attach to program 69 | { 70 | string source; 71 | 72 | if( !readSource( filename, source )) 73 | { 74 | return; 75 | } 76 | 77 | if( program == 0 ) 78 | { 79 | program = glCreateProgram(); 80 | } 81 | 82 | if( shader != 0 ) 83 | { 84 | glDetachShader( program, shader ); 85 | } 86 | 87 | shader = glCreateShader( shaderType ); 88 | const char* source_c_str = source.c_str(); 89 | glShaderSource( shader, 1, &(source_c_str), NULL ); 90 | 91 | glCompileShader( shader ); 92 | GLint compileStatus; 93 | glGetShaderiv( shader, GL_COMPILE_STATUS, &compileStatus ); 94 | 95 | if( compileStatus == GL_TRUE ) 96 | { 97 | glAttachShader( program, shader ); 98 | linked = false; 99 | } 100 | else 101 | { 102 | GLsizei maxLength = 0; 103 | glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength ); 104 | 105 | if( maxLength > 0 ) 106 | { 107 | GLchar* infoLog = new char[ maxLength ]; 108 | GLsizei length; 109 | 110 | glGetShaderInfoLog( shader, maxLength, &length, infoLog ); 111 | 112 | cerr << "GLSL Error: " << infoLog << endl; 113 | 114 | delete[] infoLog; 115 | } 116 | } 117 | } 118 | 119 | bool Shader::readSource( const char* filename, std::string& source ) 120 | // reads GLSL source file into a string 121 | { 122 | source = ""; 123 | 124 | ifstream in( filename ); 125 | if( !in.is_open() ) 126 | { 127 | cerr << "Error: could not open shader file "; 128 | cerr << filename; 129 | cerr << " for input!" << endl; 130 | return false; 131 | } 132 | 133 | string line; 134 | while( getline( in, line )) 135 | { 136 | source += line; 137 | } 138 | 139 | return true; 140 | } 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/Complex.cpp: -------------------------------------------------------------------------------- 1 | #include "Complex.h" 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace DDG 8 | { 9 | Complex::Complex( double a, double b ) 10 | // constructs number a+bi 11 | : re( a ), im( b ) 12 | {} 13 | 14 | void Complex::operator+=( const Complex& z ) 15 | // add z 16 | { 17 | re += z.re; 18 | im += z.im; 19 | } 20 | 21 | void Complex::operator-=( const Complex& z ) 22 | // subtract z 23 | { 24 | re -= z.re; 25 | im -= z.im; 26 | } 27 | 28 | void Complex::operator*=( const Complex& z ) 29 | // Complex multiply by z 30 | { 31 | double a = re; 32 | double b = im; 33 | double c = z.re; 34 | double d = z.im; 35 | 36 | re = a*c-b*d; 37 | im = a*d+b*c; 38 | } 39 | 40 | void Complex::operator*=( double r ) 41 | // scalar multiply by r 42 | { 43 | re *= r; 44 | im *= r; 45 | } 46 | 47 | void Complex::operator/=( double r ) 48 | // scalar divide by r 49 | { 50 | re /= r; 51 | im /= r; 52 | } 53 | 54 | void Complex::operator/=( const Complex& z ) 55 | // scalar divide by r 56 | { 57 | *this *= z.inv(); 58 | } 59 | 60 | Complex Complex::operator-( void ) const 61 | { 62 | return Complex( -re, -im ); 63 | } 64 | 65 | Complex Complex::conj( void ) const 66 | // returns Complex conjugate 67 | { 68 | return Complex( re, -im ); 69 | } 70 | 71 | Complex Complex::inv( void ) const 72 | // returns inverse 73 | { 74 | return this->conj() / this->norm2(); 75 | } 76 | 77 | double Complex::arg( void ) const 78 | // returns argument 79 | { 80 | return atan2( im, re ); 81 | } 82 | 83 | double Complex::norm( void ) const 84 | // returns norm 85 | { 86 | return sqrt( re*re + im*im ); 87 | } 88 | 89 | double Complex::norm2( void ) const 90 | // returns norm squared 91 | { 92 | return re*re + im*im; 93 | } 94 | 95 | Complex Complex::unit( void ) const 96 | // returns complex number with unit norm and same modulus 97 | { 98 | return *this / this->norm(); 99 | } 100 | 101 | Complex exp( const Complex& z ) 102 | // complex exponentiation 103 | { 104 | return ::exp( z.re ) * Complex( cos( z.im ), sin( z.im )); 105 | } 106 | 107 | Complex operator+( const Complex& z1, const Complex& z2 ) 108 | // binary addition 109 | { 110 | Complex z = z1; 111 | z += z2; 112 | return z; 113 | } 114 | 115 | Complex operator-( const Complex& z1, const Complex& z2 ) 116 | // binary subtraction 117 | { 118 | Complex z = z1; 119 | z -= z2; 120 | return z; 121 | } 122 | 123 | Complex operator*( const Complex& z1, const Complex& z2 ) 124 | // binary Complex multiplication 125 | { 126 | Complex z = z1; 127 | z *= z2; 128 | return z; 129 | } 130 | 131 | Complex operator*( const Complex& z, double r ) 132 | // right scalar multiplication 133 | { 134 | Complex zr = z; 135 | zr *= r; 136 | return zr; 137 | } 138 | 139 | Complex operator*( double r, const Complex& z ) 140 | // left scalar multiplication 141 | { 142 | return z*r; 143 | } 144 | 145 | Complex operator/( const Complex& z, double r ) 146 | // scalar division 147 | { 148 | Complex zr = z; 149 | zr /= r; 150 | return zr; 151 | } 152 | 153 | Complex operator/( const Complex& z1, const Complex& z2 ) 154 | // complex division 155 | { 156 | Complex z = z1; 157 | z /= z2; 158 | return z; 159 | } 160 | 161 | double dot( const Complex& z1, const Complex& z2 ) 162 | { 163 | return z1.re*z2.re + z1.im*z2.im; 164 | } 165 | 166 | double cross( const Complex& z1, const Complex& z2 ) 167 | { 168 | return z1.re*z2.im - z1.im*z2.re; 169 | } 170 | 171 | std::ostream& operator<<( std::ostream& os, const Complex& z ) 172 | // prints components 173 | { 174 | if( z.im > 0 ) 175 | { 176 | os << z.re << " + " << z.im << "i"; 177 | } 178 | else if( z.im < 0 ) 179 | { 180 | os << z.re << " - " << -z.im << "i"; 181 | } 182 | else 183 | { 184 | os << z.re; 185 | } 186 | 187 | return os; 188 | } 189 | } 190 | 191 | -------------------------------------------------------------------------------- /include/Viewer.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // libDDG -- Viewer.h 3 | // ----------------------------------------------------------------------------- 4 | // 5 | // Viewer provides a graphical user interface (GUI) for inspecting and 6 | // interacting with a Mesh object. Viewer methods are static in order 7 | // to make them compatible with GLUT callbacks. 8 | // 9 | 10 | #ifndef DDG_VIEWER_H 11 | #define DDG_VIEWER_H 12 | 13 | #include 14 | #include 15 | #include "Mesh.h" 16 | #include "Camera.h" 17 | #include "Shader.h" 18 | 19 | namespace DDG 20 | { 21 | class Viewer 22 | { 23 | public: 24 | static void init( void ); 25 | // displays the viewer until the program ends 26 | 27 | static Mesh mesh; 28 | // surface mesh visualized by Viewer 29 | 30 | protected: 31 | // draw routines 32 | static void initGL( void ); 33 | static void initGLUT( void ); 34 | static void initGLSL( void ); 35 | static void initLighting( void ); 36 | static void drawSurface( void ); 37 | static void drawMesh( void ); 38 | static void setMeshMaterial( void ); 39 | static void updateDisplayList( void ); 40 | static void drawField( void ); 41 | static void drawPolygons( void ); 42 | static void drawWireframe( void ); 43 | static void drawHedgehog( void ); 44 | static void drawIsolatedVertices( void ); 45 | static void drawSingularities( void ); 46 | static void drawInfo( void ); 47 | 48 | enum stringAlignment 49 | { 50 | alignLeft, 51 | alignCenter, 52 | alignRight 53 | }; 54 | static void drawString( std::string s, int x, int y, stringAlignment alignment = alignLeft ); 55 | 56 | // GLUT callbacks 57 | static void display( void ); 58 | static void idle( void ); 59 | static void keyboard( unsigned char c, int x, int y ); 60 | static void special( int i, int x, int y ); 61 | static void mouse( int button, int state, int x, int y ); 62 | static void motion( int x, int y ); 63 | static void menu( int value ); 64 | static void view( int value ); 65 | 66 | // menu functions 67 | static void mSmoothField( void ); 68 | static void mToggleAlignment( void ); 69 | static void mToggleFixedBoundary( void ); 70 | static void mToggleSingularities( void ); 71 | static void mToggleHedgehog( void ); 72 | static void mResetMesh( void ); 73 | static void mWriteMesh( void ); 74 | static void mExit( void ); 75 | static void mSmoothShaded( void ); 76 | static void mTextured( void ); 77 | static void mWireframe( void ); 78 | static void mZoomIn( void ); 79 | static void mZoomOut( void ); 80 | static void mScreenshot( void ); 81 | 82 | // unique identifiers for menus 83 | enum 84 | { 85 | menuSmoothField, 86 | menuInfConfDefo, 87 | menuResetMesh, 88 | menuWriteMesh, 89 | menuExit, 90 | menuSmoothShaded, 91 | menuWireframe, 92 | menuZoomIn, 93 | menuZoomOut, 94 | menuScreenshot, 95 | menuToggleAlignment, 96 | menuToggleFixedBoundary, 97 | menuToggleHedgehog, 98 | menuToggleSingularities 99 | }; 100 | 101 | // draw state 102 | enum RenderMode 103 | { 104 | renderShaded = 0, 105 | renderWireframe = 1 106 | }; 107 | 108 | static RenderMode mode; 109 | // current render mode 110 | static bool fieldViz; 111 | static bool hedgehogViz; 112 | static bool showSingularities; 113 | 114 | static void storeViewerState( void ); 115 | static void restoreViewerState( void ); 116 | static int windowSize[2]; 117 | 118 | static Camera camera; 119 | // keeps track of view state 120 | 121 | static GLuint surfaceDL; 122 | // display list for mesh 123 | 124 | static Shader shader; 125 | // shader used to determine appearance of surface 126 | 127 | // field parameters 128 | static int fieldDegree; // degree k of k-field 129 | static bool align; // toggles alignment with curvature 130 | static bool fixBoundary; // toggles fixed boundary vectors (Dirichlet conditions) 131 | static double t; // amount of alignment 132 | static double s; // determines smoothness energy (between -1,1) 133 | }; 134 | } 135 | 136 | #endif 137 | 138 | -------------------------------------------------------------------------------- /include/DenseMatrix.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // libDDG -- DenseMatrix.h 3 | // ----------------------------------------------------------------------------- 4 | // 5 | // DenseMatrix represents an m by n (real or complex) matrix where every 6 | // entry -- including zero-valued entries -- is stored explicitly. This 7 | // class is most commonly used to represent dense vectors in sparse linear 8 | // systems (i.e., the right hand side and the solution vector). 9 | // 10 | // A real or complex matrix is allocated via 11 | // 12 | // DenseMatrix A( m, n ); 13 | // DenseMatrix A( m, n, entryComplex ); 14 | // 15 | // Matrix elements are then accessed using parenthesis, e.g., 16 | // 17 | // A(i,j) = 1; 18 | // A(i,j) += 2; 19 | // a = A(i,j); 20 | // 21 | // etc. 22 | // 23 | // DenseMatrix is interoperable with the SuiteSparse numerical linear algebra 24 | // library. In particular, dereferencing a DenseMatrix returns a cholmod_dense* 25 | // which can be used by routines in SuiteSparse. For basic operations, however, 26 | // you should not need to access this pointer explicitly -- see the solve() 27 | // method in SparseMatrix.h. 28 | // 29 | 30 | #ifndef DDG_DENSEMATRIX_H 31 | #define DDG_DENSEMATRIX_H 32 | 33 | #include 34 | #include "Types.h" 35 | 36 | #include 37 | 38 | namespace DDG 39 | { 40 | template 41 | class DenseMatrix 42 | { 43 | public: 44 | DenseMatrix( int m = 0, int n = 0 ); 45 | // initialize an mxn matrix 46 | 47 | DenseMatrix( const DenseMatrix& A ); 48 | // copy constructor 49 | 50 | const DenseMatrix& operator=( const DenseMatrix& B ); 51 | // copies B 52 | 53 | ~DenseMatrix( void ); 54 | // destructor 55 | 56 | SparseMatrix sparse( void ); 57 | // converts to a sparse matrix 58 | 59 | int nRows( void ) const; 60 | // returns the number of rows 61 | 62 | int nColumns( void ) const; 63 | // returns the number of columns 64 | 65 | int length( void ) const; 66 | // returns the size of the largest dimension 67 | 68 | void zero( const T& val = 0. ); 69 | // sets all elements to val 70 | 71 | double norm( void ) const; 72 | // returns the maximum magnitude of any entry 73 | 74 | T& operator()( int row, int col ); 75 | T operator()( int row, int col ) const; 76 | // access the specified element of the matrix (uses 0-based indexing) 77 | 78 | T& operator()( int index ); 79 | T operator()( int index ) const; 80 | // access the specified element of a vector (uses 0-based indexing) 81 | 82 | DenseMatrix transpose( void ) const; 83 | // returns the transpose of this matrix 84 | 85 | DenseMatrix operator*( const DenseMatrix& B ) const; 86 | // returns product of this matrix with B 87 | 88 | void operator*=( const T& c ); 89 | // multiplies this matrix by the scalar c 90 | 91 | void operator/=( const T& c ); 92 | // divides this matrix by the scalar c 93 | 94 | DenseMatrix operator+( const DenseMatrix& B ) const; 95 | // returns sum of this matrix with B 96 | 97 | void operator+=( const DenseMatrix& B ); 98 | // adds B to this matrix 99 | 100 | DenseMatrix operator-( const DenseMatrix& B ) const; 101 | // returns difference of this matrix with B 102 | 103 | void operator-=( const DenseMatrix& B ); 104 | // subtracts B from this matrix 105 | 106 | DenseMatrix operator-( void ) const; 107 | // returns additive inverse of this matrix 108 | 109 | cholmod_dense* to_cholmod( void ); 110 | // returns pointer to copy of matrix in CHOLMOD format 111 | 112 | const DenseMatrix& operator=( cholmod_dense* B ); 113 | // copies a cholmod_dense* into a DenseMatrix; 114 | // takes responsibility for deallocating B 115 | 116 | void normalize( void ); 117 | // divides real part by Frobenius norm of real part 118 | 119 | T sum( void ) const; 120 | // returns the sum of all entries 121 | 122 | void removeMean( void ); 123 | // removes the mean of the real part from the real part 124 | 125 | void randomize( void ); 126 | // replaces entries with uniformly distributed random real numbers in the interval [-1,1] 127 | 128 | protected: 129 | int m, n; 130 | std::vector data; 131 | cholmod_dense* cData; 132 | }; 133 | 134 | template 135 | DenseMatrix operator*( const DenseMatrix& A, const T& c ); 136 | // right scalar multiplication 137 | 138 | template 139 | DenseMatrix operator*( const T& c, const DenseMatrix& A ); 140 | // left scalar multiplication 141 | 142 | template 143 | DenseMatrix operator/( const DenseMatrix& A, const T& c ); 144 | // scalar division 145 | 146 | template 147 | double dot( const DenseMatrix& x, const DenseMatrix& y ); 148 | // returns Euclidean inner product of x and y 149 | 150 | template 151 | std::ostream& operator << (std::ostream& os, const DenseMatrix& o); 152 | // prints entries 153 | } 154 | 155 | #include "DenseMatrix.inl" 156 | 157 | #endif 158 | 159 | -------------------------------------------------------------------------------- /include/Quaternion.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // libDDG -- Quaternion.h 3 | // ----------------------------------------------------------------------------- 4 | // 5 | // Quaternion represents an element of the quaternions, along with all the usual 6 | // vectors space operations (addition, multiplication by scalars, etc.). The 7 | // Hamilton product is expressed using the * operator: 8 | // 9 | // Quaternion p, q, r; 10 | // r = q * p; 11 | // 12 | // and conjugation is expressed using the method Quaternion::conj(): 13 | // 14 | // Quaternion q; 15 | // double normQSquared = -q.conj()*q; 16 | // 17 | // Individual components can be accessed in several ways: the real and imaginary 18 | // parts can be accessed using the methods Quaternion::re() and Quaternion::im(): 19 | // 20 | // Quaternion q; 21 | // double a = q.re(); 22 | // Vector b = q.im(); 23 | // 24 | // or by index: 25 | // 26 | // Quaternion q; 27 | // double a = q[0]; 28 | // double bi = q[1]; 29 | // double bj = q[2]; 30 | // double bk = q[3]; 31 | // 32 | 33 | #ifndef DDG_QUATERNION_H 34 | #define DDG_QUATERNION_H 35 | 36 | #include "Vector.h" 37 | #include 38 | 39 | namespace DDG 40 | { 41 | class Quaternion 42 | { 43 | public: 44 | Quaternion( void ); 45 | // initializes all components to zero 46 | 47 | Quaternion( const Quaternion& q ); 48 | // initializes from existing quaternion 49 | 50 | Quaternion( double s, double vi, double vj, double vk ); 51 | // initializes with specified real (s) and imaginary (v) components 52 | 53 | Quaternion( double s, const Vector& v ); 54 | // initializes with specified real (s) and imaginary (v) components 55 | 56 | Quaternion( double s ); 57 | // initializes purely real quaternion with specified real (s) component 58 | 59 | Quaternion( const Complex& z ); 60 | // yields z.re + i*z.im as a quaternion (j and k parts are therefore zero) 61 | 62 | Quaternion( const Vector& v ); 63 | // initializes purely imaginary quaternion with specified imaginary (v) component 64 | 65 | Quaternion( const Vector& e1, const Vector& e2, const Vector& e3 ); 66 | // gives the quaternion which rotates the standard frame 67 | // (i,j,k) into the given frame orthonormal frame (e1,e2,e3) 68 | // (the caller is responsible for making sure the frame is in 69 | // fact orthonormal) assuming(!!!) \tilde{v} = q v \bar{q} 70 | // type rotation 71 | 72 | const Quaternion& operator=( double s ); 73 | // assigns a purely real quaternion with real value s 74 | 75 | const Quaternion& operator=( const Vector& v ); 76 | // assigns a purely real quaternion with imaginary value v 77 | 78 | double& operator[]( int index ); 79 | // returns reference to the specified component (0-based indexing: r, i, j, k) 80 | 81 | const double& operator[]( int index ) const; 82 | // returns const reference to the specified component (0-based indexing: r, i, j, k) 83 | 84 | void toMatrix( double Q[4][4] ) const; 85 | // builds 4x4 matrix Q representing (left) quaternion multiplication 86 | 87 | double& re( void ); 88 | // returns reference to double part 89 | 90 | const double& re( void ) const; 91 | // returns const reference to double part 92 | 93 | Vector& im( void ); 94 | // returns reference to imaginary part 95 | 96 | const Vector& im( void ) const; 97 | // returns const reference to imaginary part 98 | 99 | Quaternion operator+( const Quaternion& q ) const; 100 | // addition 101 | 102 | Quaternion operator-( const Quaternion& q ) const; 103 | // subtraction 104 | 105 | Quaternion operator-( void ) const; 106 | // negation 107 | 108 | Quaternion operator*( double c ) const; 109 | // right scalar multiplication 110 | 111 | Quaternion operator/( double c ) const; 112 | // scalar division 113 | 114 | void operator+=( const Quaternion& q ); 115 | // addition / assignment 116 | 117 | void operator+=( double c ); 118 | // addition / assignment of pure real 119 | 120 | void operator-=( const Quaternion& q ); 121 | // subtraction / assignment 122 | 123 | void operator-=( double c ); 124 | // subtraction / assignment of pure real 125 | 126 | void operator*=( double c ); 127 | // scalar multiplication / assignment 128 | 129 | void operator/=( double c ); 130 | // scalar division / assignment 131 | 132 | Quaternion operator*( const Quaternion& q ) const; 133 | // Hamilton product 134 | 135 | void operator*=( const Quaternion& q ); 136 | // Hamilton product / assignment 137 | 138 | Quaternion conj( void ) const; 139 | // conjugation 140 | 141 | Quaternion inv( void ) const; 142 | // inverse 143 | 144 | double norm( void ) const; 145 | // returns Euclidean length 146 | 147 | double norm2( void ) const; 148 | // returns Euclidean length squared 149 | 150 | Quaternion unit( void ) const; 151 | // returns unit quaternion 152 | 153 | void normalize( void ); 154 | // divides by Euclidean length 155 | 156 | protected: 157 | double s; 158 | // scalar (double) part 159 | 160 | Vector v; 161 | // vector (imaginary) part 162 | }; 163 | 164 | Quaternion operator*( double c, const Quaternion& q ); 165 | // left scalar multiplication 166 | 167 | std::ostream& operator<<( std::ostream& os, const Quaternion& q ); 168 | // prints components 169 | } 170 | 171 | #endif 172 | 173 | -------------------------------------------------------------------------------- /include/Mesh.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // libDDG -- Mesh.h 3 | // ----------------------------------------------------------------------------- 4 | // 5 | // Mesh represents a polygonal surface mesh using the halfedge data structure. 6 | // It is essentially a large collection of disjoint vertices, edges, and faces 7 | // that are ``glued together'' by halfedges which encode connectivity (see 8 | // the documentation for an illustration). By construction, the halfedge data 9 | // structure cannot represent nonorientable surfaces or meshes with nonmanifold 10 | // edges. 11 | // 12 | // Mesh elements are referenced using iterators -- common usage of these 13 | // iterators is to either traverse an entire vector of mesh elements: 14 | // 15 | // // visit all vertices 16 | // for( VertexIter i = vertices.begin(); i != vertices.end(); i++ ) 17 | // { 18 | // //... 19 | // } 20 | // 21 | // or to perform a local traversal over the neighborhood of some mesh element: 22 | // 23 | // // visit both halfedges of edge e 24 | // HalfEdgeIter he = e->he; 25 | // do 26 | // { 27 | // // ... 28 | // 29 | // he = he->flip; 30 | // } 31 | // while( he != e->he ); 32 | // 33 | // (See Types.h for an explicit definition of iterator types.) 34 | // 35 | // Meshes with boundary are handled by creating an additional face for each 36 | // boundary loop (the method Face::isBoundary() determines whether a given 37 | // face is a boundary loop). Isolated vertices (i.e., vertiecs not contained 38 | // in any edge or face) reference a dummy halfedge and can be checked via 39 | // the method Vertex::isIsolated(). 40 | // 41 | 42 | #ifndef DDG_MESH_H 43 | #define DDG_MESH_H 44 | 45 | #include 46 | #include 47 | 48 | #include "HalfEdge.h" 49 | #include "Vertex.h" 50 | #include "Edge.h" 51 | #include "Face.h" 52 | #include "AliasTable.h" 53 | 54 | namespace DDG 55 | { 56 | typedef std::vector Chain; 57 | 58 | class Mesh 59 | { 60 | public: 61 | Mesh( void ); 62 | // constructs an empty mesh 63 | 64 | Mesh( const Mesh& mesh ); 65 | // constructs a copy of mesh 66 | 67 | const Mesh& operator=( const Mesh& mesh ); 68 | // copies mesh 69 | 70 | int read( const std::string& filename ); 71 | // reads a mesh from a Wavefront OBJ file; return value is nonzero 72 | // only if there was an error 73 | 74 | int write( const std::string& filename, unsigned int n ) const; 75 | // writes a mesh to a Wavefront OBJ file; return value is nonzero 76 | // only if there was an error 77 | // the value n gives the degree of the field 78 | 79 | bool reload( void ); 80 | // reloads a mesh from disk using the most recent input filename 81 | 82 | void normalize( void ); 83 | // centers around the origin and rescales to have unit radius 84 | 85 | void updateGeometry( void ); 86 | // run updateGeometry() after changing vertex positions 87 | 88 | void findGenerators( void ); 89 | void buildDualSpanningTree( void ); 90 | void buildPrimalCoTree( void ); 91 | 92 | // PS: k-vec/dir stuff 93 | // initializes up all manner of needed slots 94 | void InitKVecDirData( void ); 95 | // solves for the smoothest of degree n, energy parameter s, 96 | // direction? (or vector) 97 | void ComputeSmoothest( const unsigned int n, const double s, const bool dir ); 98 | // solves for smoothest field with boundary vectors fixed to boundary normals 99 | void ComputeSmoothestFixedBoundary( const unsigned int n, const double s, const bool dir ); 100 | // solver function when using alignment energy 101 | double SmoothestCurvatureAlignment( const unsigned int n, const double s, 102 | const double lambda, const bool dir ); 103 | 104 | // computes energy and mass matrices into the mesh to then be 105 | // loaded later into the global matrix; gets called by 106 | // SetupEnergyMatrix; 107 | void ComputeEnergyAndMass( const unsigned int n, const double s ); 108 | // loads the matrix (gets called by ComputeSmoothest) 109 | void SetupEnergyMatrix( SparseMatrix &A, SparseMatrix &M, 110 | const unsigned int n, const double s, double lambda = 0. ); 111 | // loads the matrix (gets called by ComputeSmoothestFixedBoundary) 112 | void SetupEnergyMatrixFixedBoundary( SparseMatrix &A, SparseMatrix &M, DenseMatrix &b, 113 | const unsigned int n, const double s, double lambda = 0. ); 114 | 115 | // computes indices of all triangles (gets called by ComputeSmoothest) 116 | void ComputeIndices( const unsigned int n ); 117 | 118 | // uses q slot to do alignment (gets called by InitKVecDirData) 119 | void ComputeConnectionAndHopf( void ); 120 | 121 | std::vector halfedges; 122 | std::vector vertices; 123 | std::vector edges; 124 | std::vector faces; 125 | std::vector< std::vector > gens; 126 | // storage for mesh elements 127 | 128 | // returns maximum distance from any vertex to the mesh centroid 129 | double radius; 130 | 131 | FaceCIter sampleUniform( void ); 132 | // returns a face uniformly at random relative to the surface area measure 133 | 134 | void clearSingularities( void ); 135 | 136 | protected: 137 | std::string inputFilename; 138 | 139 | int nInteriorVertices; 140 | void indexVertices(); 141 | // assign unique id to each vertex (interior first, then boundary) 142 | 143 | void updateNormals( void ); 144 | void updateRadius( void ); 145 | 146 | void buildFaceTable( void ); 147 | AliasTable faceTable; 148 | // used to sample faces uniformly at random (for visualization) 149 | }; 150 | } 151 | 152 | #endif 153 | 154 | -------------------------------------------------------------------------------- /src/Face.cpp: -------------------------------------------------------------------------------- 1 | #include "Face.h" 2 | #include "Mesh.h" 3 | #include "Vector.h" 4 | #include "Utility.h" 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | namespace DDG 12 | { 13 | // unit normal 14 | void Face::updateNormal( void ) 15 | { 16 | normal = Anormal.unit(); 17 | } 18 | 19 | // area weighted normal 20 | void Face::updateANormal( void ) 21 | { 22 | assert( he->next->next->next == he ); // assuming triangles 23 | 24 | const Vector p0 = he->vertex->position, 25 | p1 = he->next->vertex->position, 26 | p2 = he->next->next->vertex->position; 27 | 28 | Anormal = cross( p1-p0, p2-p0 )/2; 29 | } 30 | 31 | double Face::area( void ) const 32 | { 33 | return Anormal.norm(); 34 | } 35 | 36 | // mean of vertex coordinates 37 | Vector Face::barycenter( void ) const 38 | { 39 | Vector c( 0., 0., 0. ); 40 | double n = 0.; 41 | 42 | HalfEdgeCIter h = he; 43 | do 44 | { 45 | c += h->vertex->position; 46 | n += 1.; 47 | h = h->next; 48 | } 49 | while( h != he ); 50 | 51 | return c/n; 52 | } 53 | 54 | // PS only needed by old code and dual code 55 | // return the 3-space vector corresponding to the distinguised direction 56 | const Vector Face::Xvector( void ) const { 57 | return X()->next->vertex->position - X()->vertex->position; 58 | } 59 | 60 | bool Face::isBoundary( void ) const { return he->onBoundary; } 61 | 62 | // which of the 3 possible choices is v? (used to figure out whether 63 | // a given vertex is i, j, or k (so to speak) 64 | unsigned int Face::i( const VertexCIter v ) const{ 65 | const VertexCIter v0 = he->vertex, v1 = he->next->vertex, v2 = he->next->next->vertex; 66 | return v == v0 ? 0 : ( v == v1 ? 1 : ( assert( v == v2 ), 2 ) ); 67 | } 68 | 69 | void Face::InitAreaAnglesAngleSums( void ){ 70 | assert( !isBoundary() ); 71 | assert( he->next->next->next == he ); // assuming triangles 72 | 73 | // masses 74 | A = Anormal.norm(); 75 | 76 | // compute Euclidean angles and add to three corner slots 77 | const VertexIter vi = he->vertex, vj = he->next->vertex, vk = he->next->next->vertex; 78 | const Vector pi = vi->position, pj = vj->position, pk = vk->position; 79 | // normalized edges 80 | const Vector tij = pj-pi, tjk = pk-pj, tki = pi-pk; 81 | const double sinT = 2*A; 82 | vi->s += ( alpha[0] = atan2( sinT, -dot( tij, tki ) ) ); 83 | vj->s += ( alpha[1] = atan2( sinT, -dot( tjk, tij ) ) ); 84 | vk->s += ( alpha[2] = atan2( sinT, -dot( tki, tjk ) ) ); 85 | } 86 | 87 | double Face::K( void ) const{ 88 | const VertexCIter vi = he->vertex, vj = he->next->vertex, vk = he->next->next->vertex; 89 | return alpha[0] * ( vi->s - 1 ) + alpha[1] * ( vj->s - 1 ) + alpha[2] * ( vk->s - 1 ); 90 | } 91 | 92 | Vector Face :: sampleUniform( void ) const 93 | { 94 | // [This subroutine dedicated to Jim Arvo.] 95 | 96 | // Compute the warping function (xi1,xi2) -> (s,t). 97 | double xi1 = unitRand(); 98 | double xi2 = unitRand(); 99 | double s = sqrt( xi1 ); 100 | double t = xi2; 101 | 102 | // Plug the warped coords into the original parameterization. 103 | Vector A( 1., 0., 0. ); 104 | Vector B( 0., 1., 0. ); 105 | Vector C( 0., 0., 1. ); 106 | Vector P = (1.-s)*A + s*(1.-t)*B + s*t*C; 107 | 108 | return P; 109 | } 110 | 111 | Vector Face::sampleField( const Vector& p, double n ) const 112 | { 113 | using namespace DDGConstants; 114 | 115 | // TODO handle singular faces 116 | 117 | // get incident halfedges 118 | const HalfEdgeCIter h[3] = { 119 | he, 120 | he->next, 121 | he->next->next 122 | }; 123 | 124 | // get transport angles along each edge // REMOVE w 125 | Vector r( fmodPI(n*(h[0]->r()+h[0]->w())), 126 | fmodPI(n*(h[1]->r()+h[1]->w())), 127 | fmodPI(n*(h[2]->r()+h[2]->w())) ); 128 | 129 | // compute associated total curvature for the triangle 130 | double Omega = r[0] + r[1] + r[2]; 131 | 132 | // compute the right-hand side y 133 | Vector y = Omega*p - r; 134 | 135 | // compute the transport angles from each vertex to p 136 | Vector x( y[2] - y[0], 137 | y[0] - y[1], 138 | y[1] - y[2] ); 139 | x /= 3.; 140 | 141 | // // get field vectors 142 | // Vector N = normal(); 143 | // Quaternion Z[3]; 144 | // for( int i = 0; i < 3; i++ ) 145 | // { 146 | // Z[i] = h[i]->vertex->kVec(); 147 | // double a = Z[i].norm(); 148 | // Z[i].im() -= dot(Z[i].im(),N)*N; 149 | // Z[i].normalize(); 150 | // Z[i] *= a; 151 | // } 152 | 153 | // get field vectors 154 | Vector N = normal; 155 | Quaternion Z[3]; 156 | for( int i = 0; i < 3; i++ ) 157 | { 158 | HalfEdgeCIter X = h[i]->vertex->X(); 159 | double s = h[i]->vertex->s; 160 | double theta = 0.; 161 | HalfEdgeCIter Y = h[i]; 162 | do 163 | { 164 | Y = Y->flip->next; 165 | theta += Y->cornerAngle() * s; 166 | } 167 | while( Y->flip->next != X ); 168 | 169 | double phi = h[i]->vertex->u.arg() - theta; 170 | double a = h[i]->vertex->u.norm(); 171 | Quaternion w = ( h[i]->flip->vertex->position - 172 | h[i]->vertex->position ).unit(); 173 | Quaternion q( cos(phi/2.), sin(phi/2.)*N ); 174 | Z[i] = a * (q*w*q.conj()).im(); 175 | } 176 | 177 | // compute in-plane rotations by each transport angle 178 | Quaternion q[3]; 179 | q[0] = Quaternion( cos(x[0]/2.), -sin(x[0]/2.)*N ); 180 | q[1] = Quaternion( cos(x[1]/2.), -sin(x[1]/2.)*N ); 181 | q[2] = Quaternion( cos(x[2]/2.), -sin(x[2]/2.)*N ); 182 | 183 | 184 | // transport field vectors from vertices to p 185 | Quaternion Zp = p[0]*( q[0].conj()*Z[0]*q[0] ) + 186 | p[1]*( q[1].conj()*Z[1]*q[1] ) + 187 | p[2]*( q[2].conj()*Z[2]*q[2] ) ; 188 | 189 | return Zp.im(); 190 | } 191 | 192 | Vector Face :: toWorldCoordinates( const Vector& b ) const 193 | { 194 | Vector pi = he->vertex->position; 195 | Vector pj = he->next->vertex->position; 196 | Vector pk = he->next->next->vertex->position; 197 | 198 | return b[0]*pi + 199 | b[1]*pj + 200 | b[2]*pk ; 201 | } 202 | } 203 | 204 | -------------------------------------------------------------------------------- /src/Image.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | #include "Image.h" 8 | 9 | namespace DDG 10 | { 11 | Image :: Image( int width, int height ) 12 | : w( width ), h( height ), pixels( w*h*3 ) 13 | {} 14 | 15 | float& Image :: operator()( int x, int y ) 16 | // accesses pixel (x,y) 17 | { 18 | return pixels[ x + y*w ]; 19 | } 20 | 21 | const float& Image :: operator()( int x, int y ) const 22 | // accesses pixel (x,y) 23 | { 24 | return pixels[ x + y*w ]; 25 | } 26 | 27 | float Image :: sample( float x, float y ) const 28 | // samples image at (x,y) using bilinear filtering 29 | { 30 | const Image& I( *this ); 31 | float ax = x - floor( x ); 32 | float ay = y - floor( y ); 33 | float bx = 1. - ax; 34 | float by = 1. - ay; 35 | int x0 = (int) floor( x ); 36 | int y0 = (int) floor( y ); 37 | int x1 = x0 + 1; 38 | int y1 = y0 + 1; 39 | 40 | clamp( x0, y0 ); 41 | clamp( x1, y1 ); 42 | 43 | return by * ( bx * I(x0,y0) + ax * I(x1,y0) ) + 44 | ay * ( bx * I(x0,y1) + ax * I(x1,y1) ) ; 45 | } 46 | 47 | int Image :: width( void ) const 48 | // returns image width 49 | { 50 | return w; 51 | } 52 | 53 | int Image :: height( void ) const 54 | // returns image height 55 | { 56 | return h; 57 | } 58 | 59 | class TGAHeader 60 | // header format for Truevision TGA images 61 | { 62 | public: 63 | char idFieldSize; 64 | char colorMapType; 65 | char dataTypeCode; 66 | short colorMapOrigin; 67 | short colorMapLength; 68 | char colorMapEntrySize; 69 | short xOrigin; 70 | short yOrigin; 71 | short width; 72 | short height; 73 | char bitsPerPixel; 74 | char imageSpecification; 75 | }; 76 | 77 | void Image :: read( const char* filename ) 78 | // loads an image file in Truevision TGA format 79 | // (must be uncompressed RGB image with 24 or 32 bits per pixel) 80 | { 81 | ifstream in( filename, ios_base::binary ); 82 | 83 | if( !in.is_open() ) 84 | { 85 | cerr << "Error: could not open file " << filename << " for input!" << endl; 86 | exit( 1 ); 87 | } 88 | 89 | // read header 90 | TGAHeader header; 91 | in.read( (char*) &(header.idFieldSize), 1 ); 92 | in.read( (char*) &(header.colorMapType), 1 ); 93 | in.read( (char*) &(header.dataTypeCode), 1 ); 94 | in.read( (char*) &(header.colorMapOrigin), 2 ); 95 | in.read( (char*) &(header.colorMapLength), 2 ); 96 | in.read( (char*) &(header.colorMapEntrySize), 1 ); 97 | in.read( (char*) &(header.xOrigin), 2 ); 98 | in.read( (char*) &(header.yOrigin), 2 ); 99 | in.read( (char*) &(header.width), 2 ); 100 | in.read( (char*) &(header.height), 2 ); 101 | in.read( (char*) &(header.bitsPerPixel), 1 ); 102 | in.read( (char*) &(header.imageSpecification), 1 ); 103 | 104 | w = header.width; 105 | h = header.height; 106 | 107 | // validate data type 108 | const char uncompressedRGB = 2; 109 | if( header.dataTypeCode != uncompressedRGB || 110 | ( header.bitsPerPixel != 24 && 111 | header.bitsPerPixel != 32 )) 112 | { 113 | cerr << "Error: input must be uncompressed RGB image with 24 or 32 bits per pixel." << endl; 114 | exit( 1 ); 115 | } 116 | 117 | // read identification field (unused) 118 | vector idField( header.idFieldSize ); 119 | in.read( &idField[0], header.idFieldSize ); 120 | 121 | // read color map data (unused) 122 | if( header.colorMapType == 1 ) 123 | { 124 | int bytesPerEntry = header.colorMapEntrySize / 8; 125 | int colorMapSize = header.colorMapLength * bytesPerEntry; 126 | vector colorMapData( colorMapSize ); 127 | in.read( &colorMapData[0], colorMapSize ); 128 | } 129 | 130 | // read pixel data 131 | int n = w*h*header.bitsPerPixel/8; 132 | vector pixelData( n ); 133 | in.read( (char*) &pixelData[0], n ); 134 | 135 | // convert pixel data to floating point 136 | pixels.resize( n ); 137 | for( int i = 0; i < n; i++ ) 138 | { 139 | pixels[i] = (double) pixelData[i] / 255.; 140 | } 141 | } 142 | 143 | void Image :: write( const char* filename ) const 144 | // writes an image file in Truevision TGA format 145 | // (uncompressed RGB image with 24 bits per pixel) 146 | { 147 | ofstream out( filename, ios_base::binary ); 148 | 149 | if( !out.is_open() ) 150 | { 151 | cerr << "Error: could not open file " << filename << " for output!" << endl; 152 | exit( 1 ); 153 | } 154 | 155 | TGAHeader header; 156 | header.idFieldSize = 0; 157 | header.colorMapType = 0; 158 | header.dataTypeCode = 2; 159 | header.colorMapOrigin = 0; 160 | header.colorMapLength = 0; 161 | header.colorMapEntrySize = 0; 162 | header.xOrigin = 0; 163 | header.yOrigin = 0; 164 | header.width = w; 165 | header.height = h; 166 | header.bitsPerPixel = 24; 167 | header.imageSpecification = 0; 168 | 169 | // write header 170 | out.write( (char*) &(header.idFieldSize), 1 ); 171 | out.write( (char*) &(header.colorMapType), 1 ); 172 | out.write( (char*) &(header.dataTypeCode), 1 ); 173 | out.write( (char*) &(header.colorMapOrigin), 2 ); 174 | out.write( (char*) &(header.colorMapLength), 2 ); 175 | out.write( (char*) &(header.colorMapEntrySize), 1 ); 176 | out.write( (char*) &(header.xOrigin), 2 ); 177 | out.write( (char*) &(header.yOrigin), 2 ); 178 | out.write( (char*) &(header.width), 2 ); 179 | out.write( (char*) &(header.height), 2 ); 180 | out.write( (char*) &(header.bitsPerPixel), 1 ); 181 | out.write( (char*) &(header.imageSpecification), 1 ); 182 | 183 | // convert pixel data from floating point 184 | vector pixelData( w*h*3 ); 185 | for( int i = 0; i < w*h*3; i++ ) 186 | { 187 | pixelData[i] = (unsigned char)( pixels[i] * 255. ); 188 | } 189 | 190 | // write pixel data 191 | out.write( (char*) &pixelData[0], w*h*3 ); 192 | } 193 | 194 | void Image :: clamp( int& x, int& y ) const 195 | // clamps coordinates to range [0,w-1] x [0,h-1] 196 | { 197 | x = max( 0, min( w-1, x )); 198 | y = max( 0, min( h-1, y )); 199 | } 200 | } 201 | 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ####################################################################################################### 2 | 3 | # Specify library locations here (add or remove "#" marks to comment/uncomment lines for your platform) 4 | 5 | # Mac OS X 6 | DDG_INCLUDE_PATH = -I/usr/local/include 7 | DDG_LIBRARY_PATH = -L/usr/local/lib 8 | DDG_BLAS_LIBS = -framework Accelerate 9 | DDG_SUITESPARSE_LIBS = -lspqr -lumfpack -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -lm -lsuitesparseconfig 10 | DDG_OPENGL_LIBS = -framework OpenGL -framework GLUT 11 | 12 | # # Linux 13 | # DDG_INCLUDE_PATH = 14 | # DDG_LIBRARY_PATH = 15 | # DDG_BLAS_LIBS = -llapack -lblas -lgfortran 16 | # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lmetis -lcolamd -lccolamd -lcamd -lamd -lm 17 | # DDG_OPENGL_LIBS = -lglut -lGL -lGLU -lX11 18 | 19 | # # Windows / Cygwin 20 | # DDG_INCLUDE_PATH = -I/usr/include/opengl -I/usr/include/suitesparse 21 | # DDG_LIBRARY_PATH = -L/usr/lib/w32api -L/usr/lib/suitesparse 22 | # DDG_BLAS_LIBS = -llapack -lblas 23 | # DDG_SUITESPARSE_LIBS = -lspqr -lcholmod -lcolamd -lccolamd -lcamd -lamd -lm 24 | # DDG_OPENGL_LIBS = -lglut32 -lglu32 -lopengl32 25 | 26 | ####################################################################################################### 27 | 28 | TARGET = fieldviz 29 | CLTARGET = fieldgen 30 | CC = g++ 31 | LD = g++ 32 | CFLAGS = -g -std=c++11 -Wall -Wno-deprecated -Werror -Wno-error=deprecated-declarations -ansi -pedantic $(DDG_INCLUDE_PATH) -I./include -I./src 33 | LFLAGS = -g -Wall -Wno-deprecated -Werror -pedantic $(DDG_LIBRARY_PATH) 34 | LIBS = $(DDG_OPENGL_LIBS) $(DDG_SUITESPARSE_LIBS) $(DDG_BLAS_LIBS) 35 | CLLIBS = $(DDG_SUITESPARSE_LIBS) $(DDG_BLAS_LIBS) 36 | 37 | ## !! Do not edit below this line -- dependencies can be updated by running ./update ################## 38 | 39 | OBJS = obj/Camera.o obj/Complex.o obj/DenseMatrix.o obj/Edge.o obj/Face.o obj/HalfEdge.o obj/Image.o obj/KVecDir.o obj/LinearContext.o obj/Mesh.o obj/MeshIO.o obj/Quaternion.o obj/Real.o obj/SectionIntegrals.o obj/Shader.o obj/SparseMatrix.o obj/Vector.o obj/Vertex.o obj/Viewer.o obj/main.o 40 | CLOBJS = obj/Complex.o obj/DenseMatrix.o obj/Edge.o obj/Face.o obj/HalfEdge.o obj/KVecDir.o obj/LinearContext.o obj/Mesh.o obj/MeshIO.o obj/Quaternion.o obj/Real.o obj/SectionIntegrals.o obj/SparseMatrix.o obj/Vector.o obj/Vertex.o obj/commandline.o 41 | 42 | all: $(TARGET) 43 | 44 | $(TARGET): $(OBJS) 45 | $(LD) $(LFLAGS) $(OBJS) $(LIBS) -o $(TARGET) 46 | 47 | commandline: $(CLOBJS) 48 | $(LD) $(LFLAGS) $(CLOBJS) $(CLLIBS) -o $(CLTARGET) 49 | 50 | obj/Camera.o: src/Camera.cpp include/Camera.h include/Quaternion.h include/Vector.h 51 | $(CC) $(CFLAGS) -c src/Camera.cpp -o obj/Camera.o 52 | 53 | obj/Complex.o: src/Complex.cpp include/Complex.h 54 | $(CC) $(CFLAGS) -c src/Complex.cpp -o obj/Complex.o 55 | 56 | obj/DenseMatrix.o: src/DenseMatrix.cpp include/DenseMatrix.h include/Types.h src/DenseMatrix.inl include/DenseMatrix.h include/LinearContext.h include/Quaternion.h include/Vector.h include/SparseMatrix.h include/Complex.h src/SparseMatrix.inl include/Real.h include/Complex.h include/Utility.h 57 | $(CC) $(CFLAGS) -c src/DenseMatrix.cpp -o obj/DenseMatrix.o 58 | 59 | obj/Edge.o: src/Edge.cpp include/Edge.h include/Types.h include/Quaternion.h include/Vector.h include/Complex.h include/Mesh.h include/HalfEdge.h include/Vertex.h include/Edge.h include/Face.h include/AliasTable.h 60 | $(CC) $(CFLAGS) -c src/Edge.cpp -o obj/Edge.o 61 | 62 | obj/Face.o: src/Face.cpp include/Face.h include/Types.h include/Complex.h include/Vector.h include/Mesh.h include/HalfEdge.h include/Quaternion.h include/Vertex.h include/Edge.h include/Face.h include/AliasTable.h include/Vector.h include/Utility.h 63 | $(CC) $(CFLAGS) -c src/Face.cpp -o obj/Face.o 64 | 65 | obj/HalfEdge.o: src/HalfEdge.cpp include/HalfEdge.h include/Vector.h include/Types.h include/Complex.h include/Quaternion.h include/Edge.h include/Mesh.h include/HalfEdge.h include/Vertex.h include/Edge.h include/Face.h include/AliasTable.h include/Vertex.h include/Vector.h 66 | $(CC) $(CFLAGS) -c src/HalfEdge.cpp -o obj/HalfEdge.o 67 | 68 | obj/Image.o: src/Image.cpp include/Image.h 69 | $(CC) $(CFLAGS) -c src/Image.cpp -o obj/Image.o 70 | 71 | obj/KVecDir.o: src/KVecDir.cpp include/Utility.h include/Complex.h include/Complex.h include/Mesh.h include/HalfEdge.h include/Vector.h include/Types.h include/Quaternion.h include/Vertex.h include/Edge.h include/Face.h include/AliasTable.h include/SparseMatrix.h src/SparseMatrix.inl include/Real.h include/Complex.h include/SparseMatrix.h include/DenseMatrix.h src/DenseMatrix.inl include/LinearContext.h include/Quaternion.h include/Utility.h include/SectionIntegrals.h 72 | $(CC) $(CFLAGS) -c src/KVecDir.cpp -o obj/KVecDir.o 73 | 74 | obj/LinearContext.o: src/LinearContext.cpp include/LinearContext.h 75 | $(CC) $(CFLAGS) -c src/LinearContext.cpp -o obj/LinearContext.o 76 | 77 | obj/Mesh.o: src/Mesh.cpp include/Mesh.h include/HalfEdge.h include/Vector.h include/Types.h include/Complex.h include/Quaternion.h include/Vertex.h include/Edge.h include/Face.h include/AliasTable.h include/MeshIO.h 78 | $(CC) $(CFLAGS) -c src/Mesh.cpp -o obj/Mesh.o 79 | 80 | obj/MeshIO.o: src/MeshIO.cpp include/MeshIO.h include/Vector.h include/Mesh.h include/HalfEdge.h include/Types.h include/Complex.h include/Quaternion.h include/Vertex.h include/Edge.h include/Face.h include/AliasTable.h 81 | $(CC) $(CFLAGS) -c src/MeshIO.cpp -o obj/MeshIO.o 82 | 83 | obj/Quaternion.o: src/Quaternion.cpp include/Quaternion.h include/Vector.h include/Complex.h 84 | $(CC) $(CFLAGS) -c src/Quaternion.cpp -o obj/Quaternion.o 85 | 86 | obj/Real.o: src/Real.cpp include/Real.h 87 | $(CC) $(CFLAGS) -c src/Real.cpp -o obj/Real.o 88 | 89 | obj/SectionIntegrals.o: src/SectionIntegrals.cpp include/Complex.h include/SectionIntegrals.h include/Complex.h 90 | $(CC) $(CFLAGS) -c src/SectionIntegrals.cpp -o obj/SectionIntegrals.o 91 | 92 | obj/Shader.o: src/Shader.cpp include/Shader.h 93 | $(CC) $(CFLAGS) -c src/Shader.cpp -o obj/Shader.o 94 | 95 | obj/SparseMatrix.o: src/SparseMatrix.cpp include/SparseMatrix.h include/Types.h include/Complex.h src/SparseMatrix.inl include/Real.h include/Complex.h include/SparseMatrix.h include/DenseMatrix.h src/DenseMatrix.inl include/LinearContext.h include/Quaternion.h include/Vector.h include/Utility.h 96 | $(CC) $(CFLAGS) -c src/SparseMatrix.cpp -o obj/SparseMatrix.o 97 | 98 | obj/Vector.o: src/Vector.cpp include/Vector.h include/Complex.h 99 | $(CC) $(CFLAGS) -c src/Vector.cpp -o obj/Vector.o 100 | 101 | obj/Vertex.o: src/Vertex.cpp include/Vertex.h include/Vector.h include/Types.h include/Complex.h include/HalfEdge.h include/Quaternion.h include/Mesh.h include/Vertex.h include/Edge.h include/Face.h include/AliasTable.h include/HalfEdge.h include/Quaternion.h 102 | $(CC) $(CFLAGS) -c src/Vertex.cpp -o obj/Vertex.o 103 | 104 | obj/Viewer.o: src/Viewer.cpp include/Viewer.h include/Mesh.h include/HalfEdge.h include/Vector.h include/Types.h include/Complex.h include/Quaternion.h include/Vertex.h include/Edge.h include/Face.h include/AliasTable.h include/Camera.h include/Shader.h include/Image.h include/Utility.h include/SectionIntegrals.h 105 | $(CC) $(CFLAGS) -c src/Viewer.cpp -o obj/Viewer.o 106 | 107 | obj/main.o: src/main.cpp include/Viewer.h include/Mesh.h include/HalfEdge.h include/Vector.h include/Types.h include/Complex.h include/Quaternion.h include/Vertex.h include/Edge.h include/Face.h include/AliasTable.h include/Camera.h include/Shader.h include/DenseMatrix.h src/DenseMatrix.inl include/DenseMatrix.h include/LinearContext.h include/Quaternion.h include/SparseMatrix.h src/SparseMatrix.inl include/Real.h include/Complex.h include/Utility.h 108 | $(CC) $(CFLAGS) -c src/main.cpp -o obj/main.o 109 | 110 | obj/commandline.o: src/main.cpp include/Mesh.h include/HalfEdge.h include/Vector.h include/Types.h include/Complex.h include/Quaternion.h include/Vertex.h include/Edge.h include/Face.h include/Camera.h include/DenseMatrix.h src/DenseMatrix.inl include/DenseMatrix.h include/LinearContext.h include/Quaternion.h include/SparseMatrix.h src/SparseMatrix.inl include/Real.h include/Complex.h include/Utility.h 111 | $(CC) $(CFLAGS) -c src/commandline.cpp -o obj/commandline.o 112 | 113 | 114 | clean: 115 | rm -f $(OBJS) 116 | rm -f $(CLOBJS) 117 | rm -f $(TARGET) 118 | rm -f $(TARGET).exe 119 | rm -f $(CLTARGET) 120 | rm -f $(CLTARGET).exe 121 | 122 | -------------------------------------------------------------------------------- /src/Mesh.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Mesh.h" 5 | #include "MeshIO.h" 6 | 7 | using namespace std; 8 | 9 | namespace DDG 10 | { 11 | Mesh :: Mesh( void ) 12 | {} 13 | 14 | Mesh :: Mesh( const Mesh& mesh ) 15 | { 16 | *this = mesh; 17 | } 18 | 19 | class HalfEdgeIterCompare { public: bool operator()( const HalfEdgeIter& i, const HalfEdgeIter& j ) const { return &*i < &*j; } }; 20 | class HalfEdgeCIterCompare { public: bool operator()( const HalfEdgeCIter& i, const HalfEdgeCIter& j ) const { return &*i < &*j; } }; 21 | class VertexIterCompare { public: bool operator()( const VertexIter& i, const VertexIter& j ) const { return &*i < &*j; } }; 22 | class VertexCIterCompare { public: bool operator()( const VertexCIter& i, const VertexCIter& j ) const { return &*i < &*j; } }; 23 | class FaceIterCompare { public: bool operator()( const FaceIter& i, const FaceIter& j ) const { return &*i < &*j; } }; 24 | class FaceCIterCompare { public: bool operator()( const FaceCIter& i, const FaceCIter& j ) const { return &*i < &*j; } }; 25 | class EdgeIterCompare { public: bool operator()( const EdgeIter& i, const EdgeIter& j ) const { return &*i < &*j; } }; 26 | class EdgeCIterCompare { public: bool operator()( const EdgeCIter& i, const EdgeCIter& j ) const { return &*i < &*j; } }; 27 | 28 | const Mesh& Mesh :: operator=( const Mesh& mesh ) 29 | { 30 | map< HalfEdgeCIter, HalfEdgeIter, HalfEdgeCIterCompare > halfedgeOldToNew; 31 | map< VertexCIter, VertexIter, VertexCIterCompare > vertexOldToNew; 32 | map< EdgeCIter, EdgeIter, EdgeCIterCompare > edgeOldToNew; 33 | map< FaceCIter, FaceIter, FaceCIterCompare > faceOldToNew; 34 | 35 | // copy geometry from the original mesh and create a 36 | // map from pointers in the original mesh to 37 | // those in the new mesh 38 | halfedges.clear(); for( HalfEdgeCIter he = mesh.halfedges.begin(); he != mesh.halfedges.end(); he++ ) halfedgeOldToNew[ he ] = halfedges.insert( halfedges.end(), *he ); 39 | vertices.clear(); for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) vertexOldToNew[ v ] = vertices.insert( vertices.end(), *v ); 40 | edges.clear(); for( EdgeCIter e = mesh.edges.begin(); e != mesh.edges.end(); e++ ) edgeOldToNew[ e ] = edges.insert( edges.end(), *e ); 41 | faces.clear(); for( FaceCIter f = mesh.faces.begin(); f != mesh.faces.end(); f++ ) faceOldToNew[ f ] = faces.insert( faces.end(), *f ); 42 | 43 | // "search and replace" old pointers with new ones 44 | for( HalfEdgeIter he = halfedges.begin(); he != halfedges.end(); he++ ) 45 | { 46 | he->next = halfedgeOldToNew[ he->next ]; 47 | he->flip = halfedgeOldToNew[ he->flip ]; 48 | he->vertex = vertexOldToNew[ he->vertex ]; 49 | he->edge = edgeOldToNew[ he->edge ]; 50 | he->face = faceOldToNew[ he->face ]; 51 | } 52 | 53 | for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) v->he = halfedgeOldToNew[ v->he ]; 54 | for( EdgeIter e = edges.begin(); e != edges.end(); e++ ) e->he = halfedgeOldToNew[ e->he ]; 55 | for( FaceIter f = faces.begin(); f != faces.end(); f++ ) f->he = halfedgeOldToNew[ f->he ]; 56 | 57 | return *this; 58 | } 59 | 60 | int Mesh::read( const string& filename ) 61 | { 62 | inputFilename = filename; 63 | ifstream in( filename.c_str() ); 64 | 65 | if( !in.is_open() ) 66 | { 67 | cerr << "Error reading from mesh file " << filename << endl; 68 | return 1; 69 | } 70 | 71 | int rval; 72 | if( !( rval = MeshIO::read( in, *this ))) 73 | { 74 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); vi++ ){ 75 | // bends the he pointer (if needed) so as to point to the first 76 | // boundary (exitant) edge when doing a CCW traversal; this 77 | // makes boundary processing far easier 78 | vi->EnforceBoundaryHalfEdgeConvention(); 79 | } 80 | 81 | indexVertices(); 82 | updateGeometry(); 83 | } 84 | return rval; 85 | } 86 | 87 | void Mesh::indexVertices() 88 | { 89 | int nVertices = 0; 90 | 91 | // index interior vertices first 92 | for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) 93 | { 94 | if( !v->onBoundary() ) 95 | { 96 | v->id = nVertices; 97 | nVertices++; 98 | } 99 | } 100 | 101 | nInteriorVertices = nVertices; 102 | 103 | // then index boundary vertices 104 | for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) 105 | { 106 | if( v->onBoundary() ) 107 | { 108 | v->id = nVertices; 109 | nVertices++; 110 | } 111 | } 112 | } 113 | 114 | int Mesh::write( const string& filename, unsigned int n ) const 115 | // reads a mesh from a Wavefront OBJ file; return value is nonzero 116 | // only if there was an error 117 | { 118 | ofstream out( filename.c_str() ); 119 | 120 | if( !out.is_open() ) 121 | { 122 | cerr << "Error writing to mesh file " << filename << endl; 123 | return 1; 124 | } 125 | 126 | MeshIO::write( out, *this, n ); 127 | 128 | return 0; 129 | } 130 | 131 | bool Mesh::reload( void ) 132 | { 133 | return read( inputFilename ); 134 | } 135 | 136 | void Mesh::normalize( void ) 137 | { 138 | // compute center of mass 139 | Vector c( 0., 0., 0. ); 140 | for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) 141 | { 142 | c += v->position; 143 | } 144 | c /= (double) vertices.size(); 145 | 146 | // translate to origin 147 | for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) 148 | { 149 | v->position -= c; 150 | } 151 | 152 | // rescale such that the mesh sits inside the unit ball 153 | double rMax = 0.; 154 | for( VertexCIter v = vertices.begin(); v != vertices.end(); v++ ) 155 | { 156 | rMax = max( rMax, v->position.norm() ); 157 | } 158 | for( VertexIter v = vertices.begin(); v != vertices.end(); v++ ) 159 | { 160 | v->position /= rMax; 161 | } 162 | } 163 | 164 | // maximum distance from any vertex to the mesh centroid 165 | void Mesh :: updateRadius( void ) 166 | { 167 | Vector c( 0., 0., 0. ); // centroid 168 | double A = 0.; // surface area 169 | 170 | // compute centroid as integral of position over surface area 171 | for( FaceCIter f = faces.begin(); 172 | f != faces.end(); 173 | f ++ ) 174 | { 175 | if( f->isBoundary() ) continue; 176 | double Af = f->Anormal.norm(); 177 | Vector cf = f->barycenter(); 178 | 179 | A += Af; 180 | c += Af*cf; 181 | } 182 | c /= A; 183 | 184 | // compute radius 185 | double r = 0.; 186 | for( VertexCIter v = vertices.begin(); 187 | v != vertices.end(); 188 | v ++ ) 189 | { 190 | r = max( r, (v->position-c).norm() ); 191 | } 192 | 193 | radius = r; 194 | } 195 | 196 | void Mesh :: buildFaceTable( void ) 197 | { 198 | // build alias table for probability distribution over faces 199 | for( size_t i = 0; i < faces.size(); i++ ) 200 | { 201 | faceTable.push_back( faces[i].area() ); 202 | } 203 | faceTable.build(); 204 | } 205 | 206 | FaceCIter Mesh :: sampleUniform( void ) 207 | { 208 | if( faceTable.size() == 0 ) 209 | { 210 | buildFaceTable(); 211 | } 212 | 213 | return faces[faceTable.sample()].he->face; 214 | } 215 | 216 | void Mesh :: updateNormals( void ) 217 | { 218 | for( FaceIter f = faces.begin(); 219 | f != faces.end(); 220 | f ++ ) 221 | { 222 | if( f->isBoundary() ) continue; 223 | f->updateANormal(); 224 | f->updateNormal(); 225 | } 226 | 227 | for( VertexIter v = vertices.begin(); 228 | v != vertices.end(); 229 | v ++ ) 230 | { 231 | v->updateNormal(); 232 | } 233 | } 234 | 235 | void Mesh :: updateGeometry( void ) 236 | { 237 | // Note that order matters here, since 238 | // some routines use data cached in 239 | // previous routines. 240 | 241 | normalize(); 242 | updateNormals(); 243 | updateRadius(); 244 | } 245 | } 246 | 247 | -------------------------------------------------------------------------------- /src/DenseMatrix.cpp: -------------------------------------------------------------------------------- 1 | #include "DenseMatrix.h" 2 | 3 | namespace DDG 4 | { 5 | template <> 6 | cholmod_dense* DenseMatrix :: to_cholmod( void ) 7 | // returns pointer to underlying cholmod_dense data structure 8 | { 9 | if( cData ) 10 | { 11 | cholmod_l_free_dense( &cData, context ); 12 | cData = NULL; 13 | } 14 | 15 | int d = m; // leading dimension 16 | cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_REAL, context ); 17 | double* x = (double*) cData->x; 18 | 19 | for( int i = 0; i < m*n; i++ ) 20 | { 21 | x[i] = data[i]; 22 | } 23 | 24 | return cData; 25 | } 26 | 27 | template <> 28 | cholmod_dense* DenseMatrix :: to_cholmod( void ) 29 | // returns pointer to underlying cholmod_dense data structure 30 | { 31 | if( cData ) 32 | { 33 | cholmod_l_free_dense( &cData, context ); 34 | cData = NULL; 35 | } 36 | 37 | int d = m; // leading dimension 38 | cData = cholmod_l_allocate_dense( m, n, d, CHOLMOD_COMPLEX, context ); 39 | double* x = (double*) cData->x; 40 | 41 | for( int i = 0; i < m*n; i++ ) 42 | { 43 | x[i*2+0] = data[i].re; 44 | x[i*2+1] = data[i].im; 45 | } 46 | 47 | return cData; 48 | } 49 | 50 | template <> 51 | cholmod_dense* DenseMatrix :: to_cholmod( void ) 52 | // returns pointer to underlying cholmod_dense data structure 53 | { 54 | assert( nColumns() == 1 ); 55 | 56 | if( cData ) 57 | { 58 | cholmod_l_free_dense( &cData, context ); 59 | cData = NULL; 60 | } 61 | 62 | int d = m; // leading dimension 63 | cData = cholmod_l_allocate_dense( m*4, 1, d, CHOLMOD_COMPLEX, context ); 64 | double* x = (double*) cData->x; 65 | 66 | for( int i = 0; i < m*n; i++ ) 67 | { 68 | for( int k = 0; k < 4; k++ ) 69 | { 70 | x[i*4+k] = data[i][k]; 71 | } 72 | } 73 | 74 | return cData; 75 | } 76 | 77 | template <> 78 | const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) 79 | // copies a cholmod_dense* into a DenseMatrix; 80 | // takes responsibility for deallocating B 81 | { 82 | assert( B ); 83 | assert( B->xtype == CHOLMOD_REAL ); 84 | 85 | if( cData ) 86 | { 87 | cholmod_l_free_dense( &cData, context ); 88 | } 89 | cData = B; 90 | 91 | m = cData->nrow; 92 | n = cData->ncol; 93 | data.resize( m*n ); 94 | 95 | double* x = (double*) cData->x; 96 | for( int i = 0; i < m*n; i++ ) 97 | { 98 | data[i] = x[i]; 99 | } 100 | 101 | return *this; 102 | } 103 | 104 | template <> 105 | const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) 106 | // copies a cholmod_dense* into a DenseMatrix; 107 | // takes responsibility for deallocating B 108 | { 109 | assert( B ); 110 | assert( B->xtype == CHOLMOD_COMPLEX ); 111 | 112 | if( cData ) 113 | { 114 | cholmod_l_free_dense( &cData, context ); 115 | } 116 | cData = B; 117 | 118 | m = cData->nrow; 119 | n = cData->ncol; 120 | data.resize( m*n ); 121 | 122 | double* x = (double*) cData->x; 123 | for( int i = 0; i < m*n; i++ ) 124 | { 125 | data[i] = Complex( x[i*2+0], 126 | x[i*2+1] ); 127 | } 128 | 129 | return *this; 130 | } 131 | 132 | template <> 133 | const DenseMatrix& DenseMatrix :: operator=( cholmod_dense* B ) 134 | // copies a cholmod_dense* into a DenseMatrix; 135 | // takes responsibility for deallocating B 136 | { 137 | assert( B ); 138 | assert( B->xtype == CHOLMOD_REAL ); 139 | assert( B->ncol == 1 ); 140 | assert( B->nrow%4 == 0 ); 141 | 142 | if( cData ) 143 | { 144 | cholmod_l_free_dense( &cData, context ); 145 | } 146 | cData = B; 147 | 148 | m = cData->nrow/4; 149 | n = 1; 150 | data.resize( m*n ); 151 | 152 | double* x = (double*) cData->x; 153 | for( int i = 0; i < m; i++ ) 154 | { 155 | data[i] = Quaternion( x[i*4+0], 156 | x[i*4+1], 157 | x[i*4+2], 158 | x[i*4+3] ); 159 | } 160 | 161 | return *this; 162 | } 163 | 164 | template <> 165 | std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) 166 | { 167 | const int p = 16; 168 | os.precision( p ); 169 | os << fixed; 170 | 171 | os << "{"; 172 | for( int i = 0; i < o.nRows(); i++ ) 173 | { 174 | os << "{"; 175 | for( int j = 0; j < o.nColumns()-1; j++ ) 176 | { 177 | double x = o(i,j); 178 | os << " " << x << ","; 179 | } 180 | os << o(i,o.nColumns()-1) << "},"; 181 | } 182 | os << "};"; 183 | return os; 184 | } 185 | 186 | // template <> 187 | // std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) 188 | // { 189 | // const int p = 3; 190 | // os.precision( p ); 191 | // os << scientific; 192 | 193 | // for( int i = 0; i < o.nRows(); i++ ) 194 | // { 195 | // os << "[ "; 196 | // for( int j = 0; j < o.nColumns(); j++ ) 197 | // { 198 | // double x = o(i,j); 199 | 200 | // if( x == 0. ) 201 | // { 202 | // os << " 0"; 203 | // for( int k = 0; k < p+6; k++ ) 204 | // { 205 | // os << " "; 206 | // } 207 | // } 208 | // else if( x > 0. ) 209 | // { 210 | // os << " " << x << " "; 211 | // } 212 | // else 213 | // { 214 | // os << x << " "; 215 | // } 216 | // } 217 | // os << "]" << endl; 218 | // } 219 | 220 | // return os; 221 | // } 222 | 223 | template <> 224 | std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) 225 | { 226 | const int p = 2; 227 | os.precision( p ); 228 | os << scientific; 229 | 230 | for( int i = 0; i < o.nRows(); i++ ) 231 | { 232 | os << "[ "; 233 | for( int j = 0; j < o.nColumns(); j++ ) 234 | { 235 | Complex z = o(i,j); 236 | 237 | if( z.re == 0. ) 238 | { 239 | os << " 0"; 240 | for( int k = 0; k < p+5; k++ ) 241 | { 242 | os << " "; 243 | } 244 | } 245 | else if( z.re > 0. ) 246 | { 247 | os << " " << z.re; 248 | } 249 | else 250 | { 251 | os << z.re; 252 | } 253 | 254 | if( z.im == 0 ) 255 | { 256 | os << " "; 257 | } 258 | else if( z.im >= 0 ) 259 | { 260 | os << "+"; 261 | } 262 | else 263 | { 264 | os << "-"; 265 | } 266 | 267 | if( z.im == 0. ) 268 | { 269 | for( int k = 0; k < p+8; k++ ) 270 | { 271 | os << " "; 272 | } 273 | } 274 | else 275 | { 276 | os << abs( z.im ) << "i "; 277 | } 278 | } 279 | os << " ]" << endl; 280 | } 281 | 282 | return os; 283 | } 284 | 285 | template <> 286 | std::ostream& operator<< (std::ostream& os, const DenseMatrix& o) 287 | { 288 | const int p = 2; 289 | os.precision( p ); 290 | os << scientific; 291 | 292 | for( int i = 0; i < o.nRows(); i++ ) 293 | { 294 | os << "["; 295 | for( int j = 0; j < o.nColumns(); j++ ) 296 | { 297 | Quaternion q = o(i,j); 298 | 299 | os << " " << q; 300 | } 301 | os << " ]" << endl; 302 | } 303 | 304 | return os; 305 | } 306 | 307 | template <> 308 | void DenseMatrix :: randomize( void ) 309 | // replaces entries with uniformly distributed real random numbers in the interval [-1,1] 310 | { 311 | for( int i = 0; i < m*n; i++ ) 312 | { 313 | data[i] = 2.*unitRand() - 1.; 314 | } 315 | } 316 | 317 | template <> 318 | void DenseMatrix :: randomize( void ) 319 | // replaces entries with uniformly distributed real random numbers in the interval [-1,1] 320 | { 321 | for( int i = 0; i < m*n; i++ ) 322 | { 323 | data[i].re = 2.*unitRand() - 1.; 324 | data[i].im = 2.*unitRand() - 1.; 325 | } 326 | } 327 | 328 | template <> 329 | void DenseMatrix :: randomize( void ) 330 | // replaces entries with uniformly distributed real random numbers in the interval [-1,1] 331 | { 332 | for( int i = 0; i < m*n; i++ ) 333 | { 334 | for( int k = 0; k < 4; k++ ) 335 | { 336 | data[i][k] = 2.*unitRand() - 1.; 337 | } 338 | } 339 | } 340 | } 341 | 342 | -------------------------------------------------------------------------------- /src/Quaternion.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | #include "Quaternion.h" 8 | #include "Complex.h" 9 | 10 | namespace DDG 11 | { 12 | // CONSTRUCTORS ---------------------------------------------------------- 13 | 14 | Quaternion :: Quaternion( void ) 15 | // initializes all components to zero 16 | : s( 0. ), 17 | v( 0., 0., 0. ) 18 | {} 19 | 20 | Quaternion :: Quaternion( const Quaternion& q ) 21 | // initializes from existing quaternion 22 | : s( q.s ), 23 | v( q.v ) 24 | {} 25 | 26 | Quaternion :: Quaternion( double s_, double vi, double vj, double vk ) 27 | // initializes with specified double (s) and imaginary (v) components 28 | : s( s_ ), 29 | v( vi, vj, vk ) 30 | {} 31 | 32 | Quaternion :: Quaternion( double s_, const Vector& v_ ) 33 | // initializes with specified double(s) and imaginary (v) components 34 | : s( s_ ), 35 | v( v_ ) 36 | {} 37 | 38 | Quaternion :: Quaternion( double s_ ) 39 | : s( s_ ), 40 | v( 0., 0., 0. ) 41 | {} 42 | 43 | Quaternion :: Quaternion( const Vector& v_ ) 44 | : s( 0. ), 45 | v( v_ ) 46 | {} 47 | 48 | Quaternion::Quaternion( const Complex& z ) 49 | : s( z.re ), v( z.im, 0., 0. ) 50 | { 51 | } 52 | 53 | // these codes are from SIGGRAPH course notes 23, Math for SIGGRAPH, 54 | // 1989, page 203, 204 55 | Quaternion::Quaternion( const Vector& e1, const Vector& e2, const Vector& e3 ) 56 | : s(0.), v(0.,0.,0.) 57 | { 58 | assert( abs( e1.norm2() - 1 ) < 1e-12 ); 59 | assert( abs( e2.norm2() - 1 ) < 1e-12 ); 60 | assert( abs( e3.norm2() - 1 ) < 1e-12 ); 61 | assert( dot( e1, e2 ) < 1e-12 ); 62 | assert( dot( e2, e3 ) < 1e-12 ); 63 | assert( dot( e3, e1 ) < 1e-12 ); 64 | assert( abs( dot( cross( e1, e2 ), e3 ) - 1. ) < 1e-12 ); 65 | 66 | // numerically stable way to convert a 3x3 matrix given by 3 67 | // orthonormal columns into a quaternion which affects that 68 | // rotation 69 | const double m[3][3] = 70 | { { e1[0], e2[0], e3[0] }, 71 | { e1[1], e2[1], e3[1] }, 72 | { e1[2], e2[2], e3[2] } }; 73 | const double trace = m[0][0] + m[1][1] + m[2][2]; 74 | if( trace > 0. ){ 75 | double w = sqrt( trace + 1 ); 76 | s = w * .5; 77 | w = .5 / w; 78 | v[0] = ( m[2][1] - m[1][2] ) * w; 79 | v[1] = ( m[0][2] - m[2][0] ) * w; 80 | v[2] = ( m[1][0] - m[0][1] ) * w; 81 | }else{ 82 | unsigned int i = 0; 83 | if( m[1][1] > m[0][0] ) i = 1; 84 | if( m[2][2] > m[i][i] ) i = 2; 85 | const unsigned int j = ( i + 1 ) % 3; 86 | const unsigned int k = ( j + 1 ) % 3; 87 | double w = sqrt( ( m[i][i] - ( m[j][j] + m[k][k] ) ) + 1.0 ); 88 | v[i] = w * .5; 89 | w = .5 / w; 90 | s = ( m[k][j] - m[j][k] ) * w; 91 | v[j] = ( m[j][i] + m[i][j] ) * w; 92 | v[k] = ( m[k][i] + m[i][k] ) * w; 93 | } 94 | // if( s < 0 ){ (*this) *= -1; cerr << "q"; } 95 | } 96 | 97 | // ASSIGNMENT OPERATORS -------------------------------------------------- 98 | 99 | const Quaternion& Quaternion :: operator=( double _s ) 100 | // assigns a purely real quaternion with real value s 101 | { 102 | s = _s; 103 | v = Vector( 0., 0., 0. ); 104 | 105 | return *this; 106 | } 107 | 108 | const Quaternion& Quaternion :: operator=( const Vector& _v ) 109 | // assigns a purely real quaternion with imaginary value v 110 | { 111 | s = 0.; 112 | v = _v; 113 | 114 | return *this; 115 | } 116 | 117 | 118 | // ACCESSORS ------------------------------------------------------------- 119 | 120 | double& Quaternion::operator[]( int index ) 121 | // returns reference to the specified component (0-based indexing: double, i, j, k) 122 | { 123 | return ( &s )[ index ]; 124 | } 125 | 126 | const double& Quaternion::operator[]( int index ) const 127 | // returns const reference to the specified component (0-based indexing: double, i, j, k) 128 | { 129 | return ( &s )[ index ]; 130 | } 131 | 132 | void Quaternion::toMatrix( double Q[4][4] ) const 133 | // returns 4x4 matrix representation 134 | { 135 | Q[0][0] = s; Q[0][1] = -v.x; Q[0][2] = -v.y; Q[0][3] = -v.z; 136 | Q[1][0] = v.x; Q[1][1] = s; Q[1][2] = -v.z; Q[1][3] = v.y; 137 | Q[2][0] = v.y; Q[2][1] = v.z; Q[2][2] = s; Q[2][3] = -v.x; 138 | Q[3][0] = v.z; Q[3][1] = -v.y; Q[3][2] = v.x; Q[3][3] = s; 139 | } 140 | 141 | double& Quaternion::re( void ) 142 | // returns reference to double part 143 | { 144 | return s; 145 | } 146 | 147 | const double& Quaternion::re( void ) const 148 | // returns const reference to double part 149 | { 150 | return s; 151 | } 152 | 153 | Vector& Quaternion::im( void ) 154 | // returns reference to imaginary part 155 | { 156 | return v; 157 | } 158 | 159 | const Vector& Quaternion::im( void ) const 160 | // returns const reference to imaginary part 161 | { 162 | return v; 163 | } 164 | 165 | 166 | // VECTOR SPACE OPERATIONS ----------------------------------------------- 167 | 168 | Quaternion Quaternion::operator+( const Quaternion& q ) const 169 | // addition 170 | { 171 | return Quaternion( s+q.s, v+q.v ); 172 | } 173 | 174 | Quaternion Quaternion::operator-( const Quaternion& q ) const 175 | // subtraction 176 | { 177 | return Quaternion( s-q.s, v-q.v ); 178 | } 179 | 180 | Quaternion Quaternion::operator-( void ) const 181 | // negation 182 | { 183 | return Quaternion( -s, -v ); 184 | } 185 | 186 | Quaternion Quaternion::operator*( double c ) const 187 | // scalar multiplication 188 | { 189 | return Quaternion( s*c, v*c ); 190 | } 191 | 192 | Quaternion operator*( double c, const Quaternion& q ) 193 | // scalar multiplication 194 | { 195 | return q*c; 196 | } 197 | 198 | Quaternion Quaternion::operator/( double c ) const 199 | // scalar division 200 | { 201 | return Quaternion( s/c, v/c ); 202 | } 203 | 204 | void Quaternion::operator+=( const Quaternion& q ) 205 | // addition / assignment 206 | { 207 | s += q.s; 208 | v += q.v; 209 | } 210 | 211 | void Quaternion::operator+=( double c ) 212 | // addition / assignment of pure real 213 | { 214 | s += c; 215 | } 216 | 217 | void Quaternion::operator-=( const Quaternion& q ) 218 | // subtraction / assignment 219 | { 220 | s -= q.s; 221 | v -= q.v; 222 | } 223 | 224 | void Quaternion::operator-=( double c ) 225 | // subtraction / assignment of pure real 226 | { 227 | s -= c; 228 | } 229 | 230 | void Quaternion::operator*=( double c ) 231 | // scalar multiplication / assignment 232 | { 233 | s *= c; 234 | v *= c; 235 | } 236 | 237 | void Quaternion::operator/=( double c ) 238 | // scalar division / assignment 239 | { 240 | s /= c; 241 | v /= c; 242 | } 243 | 244 | 245 | // ALGEBRAIC OPERATIONS -------------------------------------------------- 246 | 247 | Quaternion Quaternion::operator*( const Quaternion& q ) const 248 | // Hamilton product 249 | { 250 | const double& s1( s ); 251 | const double& s2( q.s ); 252 | const Vector& v1( v ); 253 | const Vector& v2( q.v ); 254 | 255 | return Quaternion( s1*s2 - dot(v1,v2), s1*v2 + s2*v1 + cross(v1,v2) ); 256 | } 257 | 258 | void Quaternion::operator*=( const Quaternion& q ) 259 | // Hamilton product / assignment 260 | { 261 | *this = ( *this * q ); 262 | } 263 | 264 | Quaternion Quaternion::conj( void ) const 265 | // conjugation 266 | { 267 | return Quaternion( s, -v ); 268 | } 269 | 270 | Quaternion Quaternion::inv( void ) const 271 | { 272 | return ( this->conj() ) / this->norm2(); 273 | } 274 | 275 | 276 | // NORMS ----------------------------------------------------------------- 277 | 278 | double Quaternion::norm( void ) const 279 | // returns Euclidean length 280 | { 281 | return sqrt( s*s + v.x*v.x + v.y*v.y + v.z*v.z ); 282 | } 283 | 284 | double Quaternion::norm2( void ) const 285 | // returns Euclidean length squared 286 | { 287 | return s*s + dot(v,v); 288 | } 289 | 290 | Quaternion Quaternion::unit( void ) const 291 | // returns unit quaternion 292 | { 293 | return *this / norm(); 294 | } 295 | 296 | void Quaternion::normalize( void ) 297 | // divides by Euclidean length 298 | { 299 | *this /= norm(); 300 | } 301 | 302 | 303 | // GEOMETRIC OPERATIONS -------------------------------------------------- 304 | 305 | Quaternion slerp( const Quaternion& q0, const Quaternion& q1, double t ) 306 | // spherical-linear interpolation 307 | { 308 | // interpolate length 309 | double m0 = q0.norm(); 310 | double m1 = q1.norm(); 311 | double m = (1-t)*m0 + t*m1; 312 | 313 | // interpolate direction 314 | Quaternion p0 = q0 / m0; 315 | Quaternion p1 = q1 / m1; 316 | double theta = acos(( p0.conj()*p1 ).re() ); 317 | Quaternion p = ( sin((1-t)*theta)*p0 + sin(t*theta)*p1 )/sin(theta); 318 | 319 | return m*p; 320 | } 321 | 322 | 323 | // I/O ------------------------------------------------------------------------- 324 | 325 | std::ostream& operator<<( std::ostream& os, const Quaternion& q ) 326 | // prints components 327 | { 328 | os << "( " << q.re() << ", " << q.im() << " )"; 329 | 330 | return os; 331 | } 332 | } 333 | 334 | -------------------------------------------------------------------------------- /src/DenseMatrix.inl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | #include "DenseMatrix.h" 9 | #include "LinearContext.h" 10 | #include "Quaternion.h" 11 | #include "SparseMatrix.h" 12 | #include "Utility.h" 13 | 14 | namespace DDG 15 | { 16 | extern LinearContext context; 17 | 18 | template 19 | DenseMatrix :: DenseMatrix( int m_, int n_ ) 20 | // initialize an mxn matrix 21 | : m( m_ ), 22 | n( n_ ), 23 | cData( NULL ) 24 | { 25 | data.resize( m*n ); 26 | zero(); 27 | } 28 | 29 | template 30 | DenseMatrix :: DenseMatrix( const DenseMatrix& A ) 31 | // copy constructor 32 | : cData( NULL ) 33 | { 34 | *this = A; 35 | } 36 | 37 | template 38 | DenseMatrix :: ~DenseMatrix( void ) 39 | // destructor 40 | { 41 | if( cData != NULL ) 42 | { 43 | cholmod_l_free_dense( &cData, context ); 44 | } 45 | } 46 | 47 | template 48 | DenseMatrix DenseMatrix :: transpose( void ) const 49 | { 50 | const DenseMatrix& A( *this ); 51 | DenseMatrix AT( n, m ); 52 | 53 | for( int i = 0; i < n; i++ ) 54 | for( int j = 0; j < m; j++ ) 55 | { 56 | AT(i,j) = A(j,i).conj(); 57 | } 58 | 59 | return AT; 60 | } 61 | 62 | template 63 | SparseMatrix DenseMatrix::sparse( void ) 64 | // converts to a sparse matrix 65 | { 66 | SparseMatrix B; 67 | 68 | B = cholmod_l_dense_to_sparse( this->to_cholmod(), true, context ); 69 | 70 | return B; 71 | } 72 | 73 | template 74 | DenseMatrix DenseMatrix :: operator*( const DenseMatrix& B ) const 75 | // returns product of this matrix with B 76 | { 77 | const DenseMatrix& A( *this ); 78 | 79 | // make sure matrix dimensions agree 80 | assert( A.nColumns() == B.nRows() ); 81 | 82 | DenseMatrix AB( A.nRows(), B.nColumns() ); 83 | 84 | for( int i = 0; i < A.nRows(); i++ ) 85 | for( int j = 0; j < B.nColumns(); j++ ) 86 | for( int k = 0; k < A.nColumns(); k++ ) 87 | { 88 | AB( i, j ) += A( i, k ) * B( k, j ); 89 | } 90 | 91 | return AB; 92 | } 93 | 94 | template 95 | void DenseMatrix :: operator*=( const T& c ) 96 | { 97 | DenseMatrix& A( *this ); 98 | 99 | for( int i = 0; i < m; i++ ) 100 | for( int j = 0; j < n; j++ ) 101 | { 102 | A(i,j) *= c; 103 | } 104 | } 105 | 106 | template 107 | void DenseMatrix :: operator/=( const T& c ) 108 | { 109 | DenseMatrix& A( *this ); 110 | 111 | for( int i = 0; i < m; i++ ) 112 | for( int j = 0; j < n; j++ ) 113 | { 114 | A(i,j) /= c; 115 | } 116 | } 117 | 118 | template 119 | DenseMatrix DenseMatrix :: operator+( const DenseMatrix& B ) const 120 | // returns sum of this matrix with B 121 | { 122 | const DenseMatrix& A( *this ); 123 | 124 | // make sure matrix dimensions agree 125 | assert( A.nRows() == B.nRows() ); 126 | assert( A.nColumns() == B.nColumns() ); 127 | 128 | DenseMatrix C( nRows(), nColumns() ); 129 | 130 | for( int i = 0; i < nRows(); i++ ) 131 | for( int j = 0; j < nColumns(); j++ ) 132 | { 133 | C(i,j) = A(i,j) + B(i,j); 134 | } 135 | 136 | return C; 137 | } 138 | 139 | template 140 | void DenseMatrix :: operator+=( const DenseMatrix& B ) 141 | { 142 | DenseMatrix& A( *this ); 143 | 144 | // make sure matrix dimensions agree 145 | assert( A.nRows() == B.nRows() ); 146 | assert( A.nColumns() == B.nColumns() ); 147 | 148 | for( int i = 0; i < nRows(); i++ ) 149 | for( int j = 0; j < nColumns(); j++ ) 150 | { 151 | A(i,j) += B(i,j); 152 | } 153 | } 154 | 155 | template 156 | DenseMatrix DenseMatrix :: operator-( const DenseMatrix& B ) const 157 | // returns difference of this matrix with B 158 | { 159 | const DenseMatrix& A( *this ); 160 | 161 | // make sure matrix dimensions agree 162 | assert( A.nRows() == B.nRows() ); 163 | assert( A.nColumns() == B.nColumns() ); 164 | 165 | DenseMatrix C( nRows(), nColumns() ); 166 | 167 | for( int i = 0; i < nRows(); i++ ) 168 | for( int j = 0; j < nColumns(); j++ ) 169 | { 170 | C(i,j) = A(i,j) - B(i,j); 171 | } 172 | 173 | return C; 174 | } 175 | 176 | template 177 | void DenseMatrix :: operator-=( const DenseMatrix& B ) 178 | { 179 | DenseMatrix& A( *this ); 180 | 181 | // make sure matrix dimensions agree 182 | assert( A.nRows() == B.nRows() ); 183 | assert( A.nColumns() == B.nColumns() ); 184 | 185 | for( int i = 0; i < nRows(); i++ ) 186 | for( int j = 0; j < nColumns(); j++ ) 187 | { 188 | A(i,j) -= B(i,j); 189 | } 190 | } 191 | 192 | template 193 | DenseMatrix operator*( const T& c, const DenseMatrix& A ) 194 | { 195 | DenseMatrix cA = A; 196 | 197 | cA *= c; 198 | 199 | return cA; 200 | } 201 | 202 | template 203 | DenseMatrix operator*( const DenseMatrix& A, double c ) 204 | { 205 | return c*A; 206 | } 207 | 208 | template 209 | DenseMatrix operator/( const DenseMatrix& A, double c ) 210 | { 211 | DenseMatrix Ac = A; 212 | 213 | Ac /= c; 214 | 215 | return Ac; 216 | } 217 | 218 | template 219 | const DenseMatrix& DenseMatrix :: operator=( const DenseMatrix& B ) 220 | // copies B 221 | { 222 | if( cData ) 223 | { 224 | cholmod_l_free_dense( &cData, context ); 225 | cData = NULL; 226 | } 227 | 228 | m = B.m; 229 | n = B.n; 230 | data = B.data; 231 | 232 | return *this; 233 | } 234 | 235 | template 236 | int DenseMatrix :: nRows( void ) const 237 | // returns the number of rows 238 | { 239 | return m; 240 | } 241 | 242 | template 243 | int DenseMatrix :: nColumns( void ) const 244 | // returns the number of columns 245 | { 246 | return n; 247 | } 248 | 249 | template 250 | int DenseMatrix :: length( void ) const 251 | // returns the size of the largest dimension 252 | { 253 | return max( m, n ); 254 | } 255 | 256 | template 257 | void DenseMatrix :: zero( const T& val ) 258 | // sets all elements to val 259 | { 260 | for( int i = 0; i < m*n; i++ ) 261 | { 262 | data[i] = val; 263 | } 264 | } 265 | 266 | template 267 | double DenseMatrix :: norm( void ) const 268 | // returns the maximum magnitude of any entry 269 | { 270 | double maxNorm = 0.; 271 | 272 | for( int i = 0; i < m*n; i++ ) 273 | { 274 | maxNorm = max( maxNorm, data[i].norm() ); 275 | } 276 | 277 | return maxNorm; 278 | } 279 | 280 | template 281 | T& DenseMatrix :: operator()( int row, int col ) 282 | { 283 | assert( 0 <= row && row < m && 0 <= col && col < n ); 284 | return data[row+m*col]; 285 | } 286 | 287 | template 288 | T DenseMatrix :: operator()( int row, int col ) const 289 | { 290 | assert( 0 <= row && row < m && 0 <= col && col < n ); 291 | return data[row+m*col]; 292 | } 293 | 294 | template 295 | T& DenseMatrix :: operator()( int index ) 296 | { 297 | assert( 0 <= index && index < m*n ); 298 | return data[index]; 299 | } 300 | 301 | template 302 | T DenseMatrix :: operator()( int index ) const 303 | { 304 | assert( 0 <= index && index < m*n ); 305 | return data[index]; 306 | } 307 | 308 | template 309 | void DenseMatrix::normalize( void ) 310 | { 311 | double rFrobeniusNorm = 0.; 312 | 313 | for( int i = 0; i < m*n; i++ ) 314 | { 315 | rFrobeniusNorm += data[i].norm2(); 316 | } 317 | 318 | rFrobeniusNorm = 1./sqrt( rFrobeniusNorm ); 319 | 320 | for( int i = 0; i < m*n; i++ ) 321 | { 322 | data[i] *= rFrobeniusNorm; 323 | } 324 | } 325 | 326 | template 327 | T DenseMatrix::sum( void ) const 328 | // returns the sum of all entries 329 | { 330 | T total( 0., 0. ); 331 | 332 | for( int i = 0; i < m*n; i++ ) 333 | { 334 | total += data[i]; 335 | } 336 | 337 | return total; 338 | } 339 | 340 | template 341 | void DenseMatrix :: removeMean( void ) 342 | { 343 | T mean = 0.; 344 | int N = m*n; 345 | 346 | for( int i = 0; i < N; i++ ) 347 | { 348 | mean += data[i]; 349 | } 350 | 351 | mean /= (double) N; 352 | 353 | for( int i = 0; i < N; i++ ) 354 | { 355 | data[i] -= mean; 356 | } 357 | } 358 | 359 | template 360 | double dot( const DenseMatrix& x, const DenseMatrix& y ) 361 | // returns Euclidean inner product of x and y 362 | { 363 | return ( x.transpose() * y )(0); 364 | } 365 | 366 | template 367 | DenseMatrix DenseMatrix::operator-( void ) const 368 | // returns additive inverse of this matrix 369 | { 370 | const DenseMatrix& A( *this ); 371 | DenseMatrix B( m, n ); 372 | 373 | for( int i = 0; i < m; i++ ) 374 | for( int j = 0; j < n; j++ ) 375 | { 376 | B( i, j ) = -A( i, j ); 377 | } 378 | 379 | return B; 380 | } 381 | } 382 | 383 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fieldgen v0.02 2 | -------------------------------------------- 3 | **authors:** Keenan Crane, Peter Schröder 4 | 5 | ![fieldgen](icon.svg) 6 | 7 | ## About 8 | 9 | Given a triangulated surface, `fieldgen` computes the smoothest unit vector 10 | field, or more generally, the smoothest unit _n_-vector field (e.g., _n_=1,2,4 11 | for unit vector, line, and cross fields, respectively). Singularities, 12 | such as sources and sinks, are automatically placed in locations that allow 13 | the field to achieve optimal smoothness. Such fields can then be used for a 14 | wide variety of computer graphics and geometry processing tasks such as 15 | surface parameterization, quad meshing, architectural geometry, anisotropic 16 | shading, and texture synthesis. 17 | 18 | The code is a reference implementation of the paper 19 | 20 | >Felix Knöppel, Keenan Crane, Ulrich Pinkall, Peter Schröder 21 | ["Globally Optimal Direction Fields"](http://www.cs.cmu.edu/~kmcrane/Projects/GloballyOptimalDirectionFields/paper.pdf) 22 | SIGGRAPH 2013 23 | 24 | This version carefully implements the finite element connection Laplacian as 25 | described in the paper, using Chebyshev expansions to ensure good numerics. It 26 | also supports some sophisticated features, such as holomorphic/anti-holomorphic 27 | energy, alignment with principal curvature directions, and alignment with the 28 | boundary. 29 | 30 | The code itself is somewhat messy research code with a very basic user 31 | interface. Several other implementations are available, which use a less 32 | sophisticated discretization of the connection Laplacian and do not support 33 | some of the features mentioned above. However, they may be useful for certain 34 | tasks, or in different build settings. In particular: 35 | 36 | - The `stripes` code provides a simple version of the algorithm, 37 | as well as editing of singularities and generation of a field- 38 | aligned parameterization: 39 | 40 | 41 | 42 | - There is also an implementation in _Directional_, which is built on 43 | top of [Eigen](http://eigen.tuxfamily.org), and is hence header-only: 44 | 45 | 46 | 47 | Note that `fieldgen` and stripes are both built on top of the CHOLMOD sparse 48 | direct solver, whereas Directional is built on top of Eigen. The latter can be 49 | easier to install (since it is header only) but can be significantly slower, 50 | since the Eigen Cholseky solver is much less mature than CHOLMOD. See also 51 | below for easy install instructions for CHOLMOD. 52 | 53 | ### Version History 54 | 55 | * 0.01 (Sep 1, 2013) — Initial release 56 | * 0.02 (Jun 4, 2019) — Added boundary alignment, OBJ output, command line support 57 | 58 | ## Installation 59 | 60 | fieldgen depends on SuiteSparse, which you can obtain from 61 | 62 | 63 | 64 | On most platforms, SuiteSparse can be installed via standard 65 | package managers. On Mac OS X / HomeBrew, it can be installed via 66 | 67 | ```brew install homebrew/science/suite-sparse``` 68 | 69 | To build, you will have to edit the Makefile and set the include/lib 70 | paths accordingly. Some examples are provided. Once these paths 71 | have been set, simply type 72 | 73 | ```make``` 74 | 75 | which (barring any compilation/linker errors) should build an executable 76 | called `fieldviz`. 77 | 78 | 79 | ## Running 80 | 81 | Once built, you should be able to run the executable by typing 82 | 83 | ```./fieldviz data/bunny.obj``` 84 | 85 | (or specifying a path to any mesh file in OBJ format). You should 86 | see a window showing the mesh and some information in the upper-left 87 | corner. Hitting `space` will generate the smoothest field on the surface: 88 | 89 | ![interface](interface.jpg) 90 | 91 | Other commands can be accessed via the keyboard: 92 | 93 | | key | action 94 | | -------- | ----------------------------------------------------------------------------- 95 | | `space` | update field 96 | | `k/K` | increase/decrease the symmetry degree of the field (1=vector, 2=line, 4=cross) 97 | | `s/S` | adjust the smoothness energy; -1=holomorphic, 0=Dirichlet, 1=antiholomorphic 98 | | `t/T` | adjust trade off between smoothness and curvature alignment (if enabled) 99 | | `c` | toggle curvature alignment 100 | | `b` | toggle boundary alignment 101 | | `m` | draw smooth shaded 102 | | `f` | draw faceted (with wireframe) 103 | | `*` | show/hide singularities 104 | | `w` | write solution to `out.obj` 105 | | `` ` `` | take a screenshot 106 | | `escape` | exit 107 | 108 | **Note:** curvature alignment works only when the symmetry degree of the field is 2 or 4. 109 | 110 | ### Input 111 | 112 | `fieldgen` assumes that the input is an [oriented](https://en.wikipedia.org/wiki/Orientability) and [manifold](http://15462.courses.cs.cmu.edu/fall2018/lecture/meshes/slide_013) triangle mesh, with or without boundary. Meshes should be specified in the [Wavefront OBJ file format](https://en.wikipedia.org/wiki/Wavefront_.obj_file). Note that the resolution of the input mesh will affect the resolution of the output field, since one vector is computed per vertex. Also note that extremely poor-quality meshes (e.g., with near-zero angles or triangle areas) might cause problems for field generation, though generally the algorithm is very robust (see in particular Figure 16 of the paper by Knöppel et al, listed above). 113 | 114 | ### Output 115 | 116 | Hitting the `w` key will write the current field (and the mesh) to the file 117 | `out.obj` in the working directory. Files are written as triangle meshes in 118 | [Wavefront OBJ format](https://en.wikipedia.org/wiki/Wavefront_.obj_file). 119 | They also include a tangent vector field, encoded in comment lines at the end 120 | of the file. The degree of the field is specified by a line of the form 121 | 122 | ```degree n``` 123 | 124 | where (for instance) _n_=1 is a unit vector field, _n_=2 is a 125 | line field, and _n_=4 is a cross field. Individual vectors 126 | are then specified by lines of the form 127 | 128 | ```field i x y z``` 129 | 130 | where `i` is the index of the vertex, and `x` `y` `z` are the three components of the tangent vector. In the case where these vectors encode an _n_-direction field this vector is just one of the _n_ possible vectors. The other vectors can be obtained by rotating this one around the corresponding vertex normal, which is given in the usual `vn` line. Singularities in the field, which are associated with faces, are indicated by lines 131 | 132 | singularity `i` `s` 133 | 134 | where `i` is the index of the triangle, and `s` is the degree of the singularity. All indices are 1-based rather than 0-based. 135 | 136 | ## Command Line 137 | 138 | A command line version of `fieldgen` is also available, which can be useful when running in batch mode, over a network, or in other situations where OpenGL visualization is not available or appropriate (e.g., bundling `fieldgen` into a plugin). 139 | 140 | **To build:** follow the same instructions above, but type 141 | 142 | ```make commandline``` 143 | 144 | instead of just `make`. Doing so should produce an executable called `fieldgen` (rather than `fieldviz`). 145 | 146 | **To run:** the only mandatory arguments are paths to the input and output meshes (in OBJ format); by default, `fieldgen` will then compute the smoothest unit vector field. To get a full set of options, type 147 | 148 | ```./fieldgen``` 149 | 150 | which should print the usage string 151 | 152 | ```usage: ./fieldgen OBJ_INPUT_PATH OBJ_OUTPUT_PATH``` 153 | ``` [--degree=n] [--alignToCurvature] [--alignToBoundary] [--s=S] [--t=T]``` 154 | 155 | The command line options are as follows: 156 | 157 | * `degree` — field degree (1=unit vector field, 2=line field, 4=cross field, etc.) 158 | * `alignToCurvature` — align field to principal curvature directions 159 | * `alignToBoundary` — align field to the domain boundary 160 | * `s/S` — controls the smoothness energy; -1=holomorphic, 0=Dirichlet, 1=antiholomorphic 161 | * `t/T` — controls the trade off between smoothness and curvature alignment (if enabled) 162 | 163 | Note that enabling boundary alignment will override curvature alignment. 164 | 165 | ## Source 166 | 167 | Much of the source code in this archive is just there to support basic stuff 168 | like loading a mesh, solving a linear system, etc. The key routines are all in 169 | 170 | * `Mesh.cpp` 171 | * `KVecDir.cpp` 172 | * `SectionIntegrals.cpp` 173 | 174 | The main routines are 175 | 176 | 177 | * `Mesh::InitKVecDirData()` — setup 178 | * `Mesh::ComputeSmoothest()` — computes smoothest field 179 | * `Mesh::ComputeSmoothestFixedBoundary()` — computes smoothest field aligned to the boundary 180 | * `Mesh::SmoothestCurvatureAlignment()` — computes curvature-aligned field 181 | 182 | An example of how these routines should be called is found in `Viewer::mSmoothField()`. 183 | 184 | 185 | ## License 186 | 187 | This code is covered by a standard MIT license. 188 | 189 | ``` 190 | Copyright (c) 2013 Keenan Crane and Peter Schröder. 191 | 192 | Permission is hereby granted, free of charge, to any person obtaining a copy 193 | of this software and associated documentation files (the "Software"), to deal 194 | in the Software without restriction, including without limitation the rights 195 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 196 | copies of the Software, and to permit persons to whom the Software is 197 | furnished to do so, subject to the following conditions: 198 | 199 | The above copyright notice and this permission notice shall be included in all 200 | copies or substantial portions of the Software. 201 | 202 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 203 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 204 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 205 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 206 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 207 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 208 | SOFTWARE. 209 | ``` 210 | 211 | -------------------------------------------------------------------------------- /src/SparseMatrix.cpp: -------------------------------------------------------------------------------- 1 | #include "SparseMatrix.h" 2 | 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | namespace DDG 9 | { 10 | template <> 11 | const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) 12 | { 13 | assert( B ); 14 | assert( B->xtype == CHOLMOD_REAL ); 15 | 16 | if( cData ) 17 | { 18 | cholmod_l_free_sparse( &cData, context ); 19 | } 20 | cData = B; 21 | 22 | m = cData->nrow; 23 | n = cData->ncol; 24 | resize( m, n ); 25 | 26 | double* pr = (double*) cData->x; 27 | SuiteSparse_long* ir = (SuiteSparse_long*) cData->i; 28 | SuiteSparse_long* jc = (SuiteSparse_long*) cData->p; 29 | 30 | // iterate over columns 31 | for( int col = 0; col < n; col++ ) 32 | { 33 | // iterate over nonzero rows 34 | for( int k = jc[col]; k < jc[col+1]; k++ ) 35 | { 36 | int row = ir[k]; 37 | 38 | (*this)( row, col ) = pr[k]; 39 | } 40 | } 41 | 42 | return *this; 43 | } 44 | 45 | template <> 46 | const SparseMatrix& SparseMatrix :: operator=( cholmod_sparse* B ) 47 | { 48 | assert( B ); 49 | assert( B->xtype == CHOLMOD_COMPLEX ); 50 | 51 | if( cData ) 52 | { 53 | cholmod_l_free_sparse( &cData, context ); 54 | } 55 | cData = B; 56 | 57 | m = cData->nrow; 58 | n = cData->ncol; 59 | resize( m, n ); 60 | 61 | double* pr = (double*) cData->x; 62 | SuiteSparse_long* ir = (SuiteSparse_long*) cData->i; 63 | SuiteSparse_long* jc = (SuiteSparse_long*) cData->p; 64 | 65 | // iterate over columns 66 | for( int col = 0; col < n; col++ ) 67 | { 68 | // iterate over nonzero rows 69 | for( int k = jc[col]; k < jc[col+1]; k++ ) 70 | { 71 | int row = ir[k]; 72 | 73 | (*this)( row, col ) = Complex( pr[k*2+0], pr[k*2+1] ); 74 | } 75 | } 76 | 77 | return *this; 78 | } 79 | 80 | template <> 81 | cholmod_sparse* SparseMatrix :: to_cholmod( void ) 82 | { 83 | SparseMatrix A( m*4, n*4 ); 84 | 85 | for( const_iterator e = begin(); 86 | e != end(); 87 | e ++ ) 88 | { 89 | int i = e->first.second; 90 | int j = e->first.first; 91 | const Quaternion& q( e->second ); 92 | 93 | 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]; 94 | 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]; 95 | 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]; 96 | 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]; 97 | } 98 | 99 | if( cData != NULL ) 100 | { 101 | cholmod_l_free_sparse( &cData, context ); 102 | } 103 | cData = cholmod_l_copy_sparse( A.to_cholmod(), context ); 104 | return cData; 105 | } 106 | 107 | template <> 108 | void SparseMatrix :: allocateSparse( int nnz ) 109 | { 110 | int nzmax = ( nnz == -1 ? data.size() : nnz ); 111 | int sorted = true; 112 | int packed = true; 113 | int stype = 0; 114 | cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); 115 | } 116 | 117 | template <> 118 | void SparseMatrix :: allocateSparse( int nnz ) 119 | { 120 | int nzmax = ( nnz == -1 ? data.size() : nnz ); 121 | int sorted = true; 122 | int packed = true; 123 | int stype = 0; 124 | cData = cholmod_l_allocate_sparse( m, n, nzmax, sorted, packed, stype, CHOLMOD_COMPLEX, context ); 125 | } 126 | 127 | template <> 128 | void SparseMatrix :: allocateSparse( int nnz ) 129 | { 130 | int nzmax = ( nnz == -1 ? data.size() : nnz ); 131 | int sorted = true; 132 | int packed = true; 133 | int stype = 0; 134 | cData = cholmod_l_allocate_sparse( m*4, n*4, nzmax, sorted, packed, stype, CHOLMOD_REAL, context ); 135 | } 136 | 137 | template <> 138 | void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) 139 | { 140 | pr[i] = e->second; 141 | } 142 | 143 | template <> 144 | void SparseMatrix :: setEntry( const_iterator e, int i, double* pr ) 145 | { 146 | pr[i*2+0] = e->second.re; 147 | pr[i*2+1] = e->second.im; 148 | } 149 | 150 | template <> 151 | void solve( SparseMatrix& A, 152 | DenseMatrix& x, 153 | DenseMatrix& b ) 154 | // solves the sparse linear system Ax = b using sparse QR factorization 155 | { 156 | int t0 = clock(); 157 | x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); 158 | int t1 = clock(); 159 | 160 | cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; 161 | cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; 162 | cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << "\n"; 163 | cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; 164 | } 165 | 166 | template <> 167 | void solve( SparseMatrix& A, 168 | DenseMatrix& x, 169 | DenseMatrix& b ) 170 | // solves the sparse linear system Ax = b using sparse QR factorization 171 | { 172 | int t0 = clock(); 173 | x = SuiteSparseQR< complex >( A.to_cholmod(), b.to_cholmod(), context ); 174 | int t1 = clock(); 175 | 176 | cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; 177 | cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; 178 | cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (complex)" << "\n"; 179 | cout << "[qr] rank: " << (*context).SPQR_istat[4] << "\n"; 180 | } 181 | 182 | template <> 183 | void solve( SparseMatrix& A, 184 | DenseMatrix& x, 185 | DenseMatrix& b ) 186 | // solves the sparse linear system Ax = b using sparse QR factorization 187 | { 188 | int t0 = clock(); 189 | x = SuiteSparseQR( A.to_cholmod(), b.to_cholmod(), context ); 190 | int t1 = clock(); 191 | 192 | cout << "[qr] time: " << seconds( t0, t1 ) << "s" << "\n"; 193 | cout << "[qr] max residual: " << residual( A, x, b ) << "\n"; 194 | cout << "[qr] size: " << A.nRows() << " x " << A.nColumns() << " (quaternion)" << "\n"; 195 | cout << "[qr] rank: " << (*context).SPQR_istat[4]/4 << "\n"; 196 | } 197 | 198 | template <> 199 | void solveSquare( SparseMatrix& A, 200 | DenseMatrix& x, 201 | DenseMatrix& b ) 202 | // solves the sparse linear system Ax = b using sparse LU factorization 203 | { 204 | int t0 = clock(); 205 | cholmod_sparse* Ac = A.to_cholmod(); 206 | int n = Ac->nrow; 207 | SuiteSparse_long* Ap = (SuiteSparse_long*) Ac->p; 208 | SuiteSparse_long* Ai = (SuiteSparse_long*) Ac->i; 209 | double* Ax = (double*) Ac->x; 210 | void* Symbolic; 211 | void* Numeric; 212 | 213 | umfpack_zl_symbolic( n, n, Ap, Ai, Ax, NULL, &Symbolic, NULL, NULL ); 214 | umfpack_zl_numeric( Ap, Ai, Ax, NULL, Symbolic, &Numeric, NULL, NULL ); 215 | umfpack_zl_solve( UMFPACK_A, Ap, Ai, Ax, NULL, (double*) &x(0), NULL, (double*) &b(0), NULL, Numeric, NULL, NULL ); 216 | umfpack_zl_free_symbolic( &Symbolic ); 217 | umfpack_zl_free_numeric( &Numeric ); 218 | 219 | int t1 = clock(); 220 | cout << "[lu] time: " << seconds( t0, t1 ) << "s" << "\n"; 221 | cout << "[lu] max residual: " << residual( A, x, b ) << "\n"; 222 | } 223 | 224 | template<> 225 | DenseMatrix SparseMatrix :: multiply( DenseMatrix& x, bool transpose ) 226 | // returns product of this matrix with dense B; if 227 | // transpose is true, uses transport of this matrix 228 | { 229 | int s = transpose ? n : m; 230 | DenseMatrix y( s, 1 ); 231 | double alpha[2] = { 1., 0. }; 232 | double beta[2] = { 0., 0. }; 233 | 234 | cholmod_dense* yc = cholmod_l_allocate_dense( s, 1, s, CHOLMOD_COMPLEX, context ); 235 | 236 | cholmod_l_sdmult 237 | ( 238 | cData, 239 | (int)transpose, 240 | alpha, 241 | beta, 242 | x.to_cholmod(), 243 | yc, 244 | context 245 | ); 246 | 247 | // convert from CHOLMOD format to DDG:DenseMatrix 248 | y = yc; 249 | 250 | return y; 251 | } 252 | 253 | template<> 254 | void SparseMatrix :: appendCompressedColumn( vector& column ) 255 | { 256 | int& i( lastCompressedEntry ); 257 | int& j( lastCompressedColumn ); 258 | 259 | double* pr = (double*) cData->x; 260 | SuiteSparse_long* ir = (SuiteSparse_long*) cData->i; 261 | SuiteSparse_long* jc = (SuiteSparse_long*) cData->p; 262 | 263 | sort( column.begin(), column.end() ); 264 | 265 | jc[j] = i; 266 | 267 | for( vector::const_iterator e = column.begin(); 268 | e != column.end(); 269 | e ++ ) 270 | { 271 | ir[i] = e->index; 272 | pr[i*2+0] = e->value.re; 273 | pr[i*2+1] = e->value.im; 274 | i++; 275 | } 276 | j++; 277 | 278 | jc[j] = i; // Yes, again. 279 | } 280 | 281 | template<> 282 | void SparseMatrix :: printCompressed( void ) const 283 | { 284 | ofstream out( "matrix.txt" ); 285 | 286 | double* pr = (double*) cData->x; 287 | SuiteSparse_long* ir = (SuiteSparse_long*) cData->i; 288 | SuiteSparse_long* jc = (SuiteSparse_long*) cData->p; 289 | 290 | for( int j = 0; j < n; j++ ) 291 | { 292 | out << jc[j] << " |\t"; 293 | for( int i = jc[j]; i < jc[j+1]; i++ ) 294 | { 295 | out << "\t" << ir[i] << "(" << pr[i*2+0] << "+" << pr[i*2+1] << "i)"; 296 | } 297 | out << endl; 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/SectionIntegrals.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Complex.h" 5 | #include "SectionIntegrals.h" 6 | 7 | /* 8 | * C version of r1mach and d1mach from core (netlib) 9 | * specialized for IEEE arithmetic 10 | * 11 | * MACHINE CONSTANTS (s for single, d for double) 12 | * {S|D}1MACH(1) = B**(EMIN-1), THE SMALLEST POSITIVE MAGNITUDE. 13 | * {S|D}1MACH(2) = B**EMAX*(1 - B**(-T)), THE LARGEST MAGNITUDE. 14 | * {S|D}1MACH(3) = B**(-T), THE SMALLEST RELATIVE SPACING. 15 | * {S|D}1MACH(4) = B**(1-T), THE LARGEST RELATIVE SPACING. 16 | * {S|D}1MACH(5) = LOG10(B) 17 | */ 18 | // #define M_PI 3.14159265358979323846264338328 19 | // #define M_LN2 0.693147180559945309417232121458 20 | static double mach[5] = {2.2250738585072014e-308, 21 | 1.7976931348623157e+308, 22 | 1.1102230246251565e-16, 23 | 2.2204460492503131e-16, 24 | 3.0102999566398120e-01}; 25 | 26 | static const double s11r[21] = { 27 | 0.0448875760891932036595562553276,0.0278480909574822965157922173757, 28 | 0.00394490790249120295818107628687,-0.00157697939158619172562804651751, 29 | -0.0000886578217796691901712579357311,0.0000301708056772263120428135787035, 30 | 9.521839632337438230089618156e-7,-3.00028307455805582080773625835e-7, 31 | -6.14917009583473496433650831019e-9,1.85133588988085286010092653662e-9, 32 | 2.67848449041765751590373973224e-11,-7.82394575359355297437491915705e-12, 33 | -8.44240072511090922609176843848e-14,2.41333276776166240844516922196e-14, 34 | 2.02015531985181413114834031833e-16,-5.68171271075270422851146478874e-17, 35 | -3.80082421064644521052871349836e-19,1.05551739229841670238163200361e-19, 36 | 5.7758422925275435667221605993e-22,-1.58774695838716531303310462626e-22, 37 | -7.24181766014636685673730787292e-25 38 | }; 39 | 40 | static const double s11i[21] = { 41 | 0.100116671557942715638078149123,0.0429600096728215971268270800599, 42 | -0.00799014859477407505770275088389,-0.000664114111384495427035329182866, 43 | 0.000240714510952202000864758517061,9.89085259369337382687437812294e-6, 44 | -3.22040860178194578481012477174e-6,-8.08401148192350365282200249069e-8, 45 | 2.48351290049260966544658921605e-8,4.24154988067028660399867468349e-10, 46 | -1.25611378629704490237955971836e-10,-1.56053077919196502557674988724e-12, 47 | 4.50565044006801278137904597946e-13,4.2641179237225098728291226479e-15, 48 | -1.2084245714879456268965803807e-15,-9.01338537885038989528688031325e-18, 49 | 2.5180796700698002962991581923e-18,1.51955263898294940481729370636e-20, 50 | -4.19737873024216866691628952458e-21,-2.092488792285595339755624521e-23, 51 | 5.72708467031136321701747126611e-24 52 | }; 53 | 54 | static const double s12r[21] = { 55 | -0.376145877558191778393359413441,0.0775244431850198578126067647425, 56 | 0.0120396593748540634695397747695,-0.00385683684390247509721340352427, 57 | -0.000232359275790231209370627606991,0.0000697318379146209092637310696007, 58 | 2.32354473986257272021507575389e-6,-6.71692140309360615694979580992e-7, 59 | -1.43946361256617673523038166877e-8,4.06087820907414336567714443732e-9, 60 | 6.10183339004616075548375321861e-11,-1.69196418769523832825063863136e-11, 61 | -1.88669746820541798989965091628e-13,5.16473095452962111184823547686e-14, 62 | 4.45066881692009291504139737861e-16,-1.20625107617859803735741992452e-16, 63 | -8.28193837331508300767103116139e-19,2.22680015825230528892642524445e-19, 64 | 1.24755889505424049389100515561e-21,-3.33254971913153176741833960484e-22, 65 | -1.55307002839777371508497520751e-24 66 | }; 67 | 68 | static const double s12i[21] = { 69 | 0.0527472790869782317601048210983,0.00823962722148093961886198320927, 70 | -0.0205185842051817330153151013327,-0.00184683218270819613487368071941, 71 | 0.000569681886932212757533488372406,0.0000248774530818801164177266528608, 72 | -7.31121019876580624171992432347e-6,-1.92744564223806538367454388776e-7, 73 | 5.49794278719049727550379096876e-8,9.78237385539447442446850072421e-10, 74 | -2.7341624177723508216430132999e-10,-3.51839815887772323640101921381e-12, 75 | 9.68934411607055794052256859665e-13,9.45703963505047353201918875825e-15, 76 | -2.57516976113400217760868402425e-15,-1.97419921753098238455550504742e-17, 77 | 5.32820017906655555903355375475e-18,3.29581793797656865402793252539e-20, 78 | -8.83137325823594007269279476114e-21,-4.50279718100548728336329365981e-23, 79 | 1.19941679774924468309434420379e-23 80 | }; 81 | 82 | static const double m12r[21] = { 83 | 0.148523151773238914750879360089,-0.0117856118001224048185631301904, 84 | -0.00248887208039014371691400683052,0.000250045060357076469386198883676, 85 | 0.0000227217776065076434637230864113,-2.48764935230787745662127026799e-6, 86 | -1.32138506847814502856384193414e-7,1.50966754393693942843767293542e-8, 87 | 5.3472999553162661403204445045e-10,-6.26136041009708550772228055719e-11, 88 | -1.59574066624737000616598104732e-12,1.89788785691219687197167013023e-13, 89 | 3.66030609080549274006207730375e-15,-4.39955659500182569051978906011e-16, 90 | -6.65848768159000092224193226014e-18,8.06343127453005031535923212263e-19, 91 | 9.84397490339224661524630997726e-21,-1.19869887155210161836484730378e-21, 92 | -1.20634550494837590549640883469e-23,1.47512193662595435067359954287e-24, 93 | 1.24549093756962710863096766634e-26 94 | }; 95 | 96 | static const double m12i[21] = { 97 | -0.0454399665519585306943416687117,-0.0210517666740874019203591488894, 98 | 0.00194647501081621201871675259482,0.000253466068123907163346571754613, 99 | -0.0000268083453427538717591876419304,-1.82138740336918117478832696004e-6, 100 | 2.04357511048425337951376869602e-7,8.75944656915074206478854298947e-9, 101 | -1.01466837126303146739791005703e-9,-3.02573132377805421636557302451e-11, 102 | 3.57358222114420372764650037191e-12,7.88121312149152771558608913996e-14, 103 | -9.42758576193708862552405242331e-15,-1.60439904050827900099939709069e-16, 104 | 1.93624791035947590366500765061e-17,2.62394448214143482490534256935e-19, 105 | -3.18700789496399461681365308408e-20,-3.52400207248027768109209530864e-22, 106 | 4.30074555255053206057921088056e-23,3.95655079023456015736315286131e-25, 107 | -4.84642137915095135859812028886e-26 108 | }; 109 | 110 | /* 111 | * evaluate a chebyshev series 112 | * adapted from fortran csevl 113 | */ 114 | static double 115 | csevl( const double x, const double *cs, unsigned int n ) 116 | { 117 | double b2 = 0, b1 = 0, b0 = 0; 118 | const double twox = 2 * x; 119 | 120 | while( n-- ){ 121 | b2 = b1; 122 | b1 = b0; 123 | b0 = twox * b1 - b2 + cs[n]; 124 | } 125 | 126 | return ( b0 - b2 )/2; 127 | } 128 | 129 | /* 130 | * from the original fortran inits 131 | * april 1977 version. w. fullerton, c3, los alamos scientific lab. 132 | * 133 | * initialize the orthogonal series so that inits is the number of terms 134 | * needed to insure the error is no larger than eta. ordinarily, eta 135 | * will be chosen to be one-tenth machine precision. 136 | */ 137 | static unsigned int 138 | inits( const double *series, unsigned int n, const double eta ){ 139 | double err = 0; 140 | 141 | while( err <= eta && n-- ){ 142 | err += fabs( series[n] ); 143 | } 144 | 145 | return n++; 146 | } 147 | 148 | static DDG::Complex 149 | s11( const double t ){ 150 | // how many entries to use? 151 | static unsigned int ns11r = 0, ns11i = 0; 152 | 153 | if( !ns11r ){ 154 | ns11r = inits( s11r, sizeof s11r / sizeof *s11r, mach[2] / 10 ); 155 | ns11i = inits( s11i, sizeof s11i / sizeof *s11i, mach[2] / 10 ); 156 | } 157 | return DDG::Complex( csevl( t, s11r, ns11r ), csevl( t, s11i, ns11i ) ); 158 | } 159 | 160 | static DDG::Complex 161 | s12( const double t ){ 162 | // how many entries to use? 163 | static unsigned int ns12r = 0, ns12i = 0; 164 | 165 | if( !ns12r ){ 166 | ns12r = inits( s12r, sizeof s12r / sizeof *s12r, mach[2] / 10 ); 167 | ns12i = inits( s12i, sizeof s12i / sizeof *s12i, mach[2] / 10 ); 168 | } 169 | return DDG::Complex( csevl( t, s12r, ns12r ), csevl( t, s12i, ns12i ) ); 170 | } 171 | 172 | static DDG::Complex 173 | m12( const double t ){ 174 | // how many entries to use? 175 | static unsigned int nm12r = 0, nm12i = 0; 176 | 177 | if( !nm12r ){ 178 | nm12r = inits( m12r, sizeof m12r / sizeof *m12r, mach[2] / 10 ); 179 | nm12i = inits( m12i, sizeof m12i / sizeof *m12i, mach[2] / 10 ); 180 | } 181 | return DDG::Complex( csevl( t, m12r, nm12r ), csevl( t, m12i, nm12i ) ); 182 | } 183 | 184 | DDG::Complex 185 | DirichletIJDirect( const double s, const double gii, const double gij, const double gjj ){ 186 | const double s2(s*s); 187 | const DDG::Complex is(0,s), is3(0,s*s2); 188 | const double s4(s2*s2); 189 | return 190 | (((3*gii + 4*gij + 3*gjj) + is*(gii + gij + gjj) - is3*gij/6 191 | + DDG::Complex(cos(s),sin(s))*(-(3*gii + 4*gij + 3*gjj) 192 | + is*(2*gii + 3*gij + 2*gjj) 193 | + s2*(gii + 2*gij + gjj)/2))/s4 194 | + (gii - 2*gij + gjj)/24 - is*(gii - 2*gij + gjj)/60); 195 | } 196 | 197 | // gii = |e_{ki}|^2, gij = , gjj = |e_{jk}|^2 198 | DDG::Complex 199 | DirichletIJ( const double s, const double gii, const double gij, const double gjj ){ 200 | if( fabs(s) > M_PI ){ 201 | return DirichletIJDirect( s, gii, gij, gjj ); 202 | }else if( s > 0 ){ 203 | // between 0 and pi 204 | const double t = s*2/M_PI - 1; // normalize 205 | return ((gii + gjj)*s11(t) + gij*s12(t)).conj(); 206 | }else{ 207 | // between -pi and 0 208 | const double t = -s*2/M_PI - 1; // normalize 209 | return ((gii + gjj)*s11(t) + gij*s12(t)); 210 | } 211 | } 212 | 213 | // gjj = |e_{ij}|^2, gjk = , gkk = |e_{ki}|^2 214 | double 215 | DirichletII( const double s, const double gjj, const double gjk, const double gkk ){ 216 | return .25*((gjj-2*gjk+gkk)+s*s*(gjj+gjk+gkk)/90); 217 | } 218 | 219 | double 220 | MassII( void ){ return 1/6.; } 221 | 222 | DDG::Complex 223 | MassIJDirect( const double s ){ 224 | const double s2 = s*s; 225 | const double s4 = s2*s2; 226 | const DDG::Complex is(0,s), is3(0,s*s2); 227 | return (6*DDG::Complex(cos(s), sin(s)) - 6 - 6*is + 3*s2 + is3)/(3*s4); 228 | } 229 | 230 | DDG::Complex 231 | MassIJ( const double s ){ 232 | if( fabs(s) > M_PI ){ 233 | return MassIJDirect( s ); 234 | }else if( s > 0 ){ 235 | const double t = s/M_PI*2-1; 236 | return m12(t).conj(); 237 | }else{ 238 | const double t = -s/M_PI*2-1; 239 | return m12(t); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /include/SparseMatrix.h: -------------------------------------------------------------------------------- 1 | // -*- Mode: c++ -*- 2 | // ----------------------------------------------------------------------------- 3 | // libDDG -- SparseMatrix.h 4 | // ----------------------------------------------------------------------------- 5 | // 6 | // SparseMatrix represents an m by n (real or complex) matrix where only 7 | // nonzero entries are stored explicitly. This class is most commonly 8 | // used to represent the linear term in sparse linear systems (i.e., the matrix 9 | // part). 10 | // 11 | // A real or complex matrix is allocated via 12 | // 13 | // SparseMatrix A( m, n ); 14 | // SparseMatrix A( m, n, entryComplex ); 15 | // 16 | // Matrix elements are then accessed using parenthesis, e.g., 17 | // 18 | // A(i,j) = 1; 19 | // A(i,j) += 2; 20 | // a = A(i,j); 21 | // 22 | // etc. 23 | // 24 | // SparseMatrix is interoperable with the SuiteSparse numerical linear algebra 25 | // library. In particular, dereferencing a SparseMatrix returns a 26 | // cholmod_sparse* which can be used by routines in SuiteSparse. For basic 27 | // operations, however, you should not need to access this pointer explicitly -- 28 | // see the solve() method below. 29 | // 30 | // Internally SparseMatrix stores nonzero entries in a heap data structure; the 31 | // amortized cost of insertion is therefore no worse than the sorting cost of 32 | // putting the matrix in compressed-column order. 33 | // 34 | 35 | #ifndef DDG_SPARSE_MATRIX_H 36 | #define DDG_SPARSE_MATRIX_H 37 | 38 | #include 39 | #include 40 | #include 41 | #include "Types.h" 42 | #include "Complex.h" 43 | 44 | namespace DDG 45 | { 46 | class ColumnEntry; 47 | 48 | template 49 | class SparseMatrix 50 | { 51 | public: 52 | SparseMatrix( int m = 0, int n = 0 ); 53 | // initialize an mxn matrix 54 | 55 | SparseMatrix( const SparseMatrix& B ); 56 | // copy constructor 57 | 58 | ~SparseMatrix( void ); 59 | // destructor 60 | 61 | const SparseMatrix& operator=( const SparseMatrix& B ); 62 | // copies B 63 | 64 | const SparseMatrix& operator=( cholmod_sparse* B ); 65 | // copies a cholmod_sparse* into a SparseMatrix; 66 | // takes responsibility for deallocating B 67 | 68 | void resize( int m, int n, int nnz = -1 ); 69 | // clears and resizes to mxn matrix 70 | // nnz is an optional storage hint (maximum number of nonzeros) 71 | 72 | SparseMatrix transpose( void ) const; 73 | // returns the transpose of this matrix 74 | 75 | cholmod_sparse* to_cholmod( void ); 76 | // returns pointer to copy of matrix in compressed-column CHOLMOD format 77 | 78 | SparseMatrix operator*( const SparseMatrix& B ) const; 79 | // returns product of this matrix with sparse B 80 | 81 | DenseMatrix operator*( const DenseMatrix& B ) const; 82 | // returns product of this matrix with dense B 83 | 84 | DenseMatrix multiply( DenseMatrix& x, bool transpose = false ); 85 | 86 | void operator*=( const T& c ); 87 | // multiplies this matrix by the scalar c 88 | 89 | void operator/=( const T& c ); 90 | // divides this matrix by the scalar c 91 | 92 | void operator+=( const SparseMatrix& B ); 93 | // adds B to this matrix 94 | 95 | void operator-=( const SparseMatrix& B ); 96 | // subtracts B from this matrix 97 | 98 | SparseMatrix operator+( const SparseMatrix& B ) const; 99 | // returns sum of this matrix with B 100 | 101 | SparseMatrix operator-( const SparseMatrix& B ) const; 102 | // returns difference of this matrix with B 103 | 104 | int nRows( void ) const; 105 | // returns the number of rows 106 | 107 | int nColumns( void ) const; 108 | // returns the number of columns 109 | 110 | int length( void ) const; 111 | // returns the size of the largest dimension 112 | 113 | void zero( const T& val ); 114 | // sets all nonzero elements val 115 | 116 | void invertDiagonal( void ); 117 | // inverts diagonal elements 118 | 119 | static SparseMatrix identity( int N ); 120 | // returns the N x N identity matrix 121 | 122 | DenseMatrix full( void ) const; 123 | // converts to a dense matrix 124 | 125 | T& operator()( int row, int col ); 126 | T operator()( int row, int col ) const; 127 | // access the specified element (uses 0-based indexing) 128 | 129 | // TODO for legibility, replace w/ type where entries are named "row, 130 | // TODO col" instead of "first, second" (especially since we adopt the 131 | // TODO unorthodox convention of storing the column first) 132 | typedef std::pair EntryIndex; 133 | // convenience type for an entry index; note that we store column THEN 134 | // row, which makes it easier to build compressed column format 135 | 136 | typedef std::map EntryMap; 137 | typedef typename EntryMap::iterator iterator; 138 | typedef typename EntryMap::const_iterator const_iterator; 139 | // convenience types for storing and accessing entries 140 | 141 | iterator begin( void ); 142 | const_iterator begin( void ) const; 143 | iterator end( void ); 144 | const_iterator end( void ) const; 145 | // return iterators to first and last nonzero entries 146 | 147 | void shift( double c ); 148 | // adds c times the identity matrix to this matrix 149 | 150 | void appendCompressedColumn( std::vector& column ); 151 | void printCompressed( void ) const; 152 | 153 | protected: 154 | int m, n; 155 | EntryMap data; 156 | cholmod_sparse* cData; 157 | int lastCompressedColumn; 158 | int lastCompressedEntry; 159 | 160 | void allocateSparse( int nnz = -1 ); 161 | void setEntry( const_iterator e, int i, double* pr ); 162 | }; 163 | 164 | template 165 | SparseMatrix operator*( const SparseMatrix& A, const T& c ); 166 | // right scalar multiplication 167 | 168 | template 169 | SparseMatrix operator*( const T& c, const SparseMatrix& A ); 170 | // left scalar multiplication 171 | 172 | template 173 | SparseMatrix operator/( const SparseMatrix& A, const T& c ); 174 | // scalar division 175 | 176 | template 177 | std::ostream& operator << (std::ostream& os, const SparseMatrix& o); 178 | // prints entries 179 | 180 | template 181 | class SparseFactor 182 | { 183 | public: 184 | SparseFactor( void ); 185 | ~SparseFactor( void ); 186 | 187 | void build( SparseMatrix& A ); 188 | // factorizes positive-definite matrix A using CHOLMOD 189 | 190 | bool valid( void ) const; 191 | // returns true if the factor has been built; false otherwise 192 | 193 | cholmod_factor* to_cholmod( void ); 194 | // returns pointer to underlying cholmod_factor data structure 195 | 196 | protected: 197 | cholmod_factor *L; 198 | }; 199 | 200 | template 201 | void solve( SparseMatrix& A, 202 | DenseMatrix& x, 203 | DenseMatrix& b ); 204 | // solves the sparse linear system Ax = b using sparse QR factorization 205 | 206 | template 207 | void solveSquare( SparseMatrix& A, 208 | DenseMatrix& x, 209 | DenseMatrix& b ); 210 | // solves the sparse linear system Ax = b using sparse LU factorization 211 | 212 | template 213 | void solvePositiveDefinite( SparseMatrix& A, 214 | DenseMatrix& x, 215 | DenseMatrix& b ); 216 | // solves the positive definite sparse linear system Ax = b using sparse Cholesky factorization 217 | 218 | template 219 | void backsolvePositiveDefinite( SparseFactor& L, 220 | DenseMatrix& x, 221 | DenseMatrix& b ); 222 | // backsolves the prefactored positive definite sparse linear system LL'x = b 223 | 224 | template 225 | void smallestEig( SparseMatrix& A, 226 | DenseMatrix& x, 227 | bool ignoreConstantVector = true ); 228 | // solves A x = lambda x for the smallest nonzero eigenvalue lambda 229 | // A must be symmetric; x is used as an initial guess 230 | 231 | template 232 | void smallestEig( SparseMatrix& A, 233 | SparseMatrix& B, 234 | DenseMatrix& x ); 235 | // solves A x = lambda B x for the smallest nonzero generalized eigenvalue lambda 236 | // A and B must be symmetric; x is used as an initial guess 237 | 238 | template 239 | void smallestEigPositiveDefinite( SparseMatrix& A, 240 | DenseMatrix& x, 241 | bool ignoreConstantVector = true ); 242 | // solves A x = lambda x for the smallest nonzero eigenvalue lambda 243 | // A must be positive (semi-)definite; x is used as an initial guess 244 | 245 | template 246 | void smallestEigPositiveDefinite( SparseMatrix& A, 247 | SparseMatrix& B, 248 | DenseMatrix& x ); 249 | // solves A x = lambda B x for the smallest nonzero eigenvalue lambda 250 | // A must be positive (semi-)definite, B must be symmetric; x is used as an initial guess 251 | 252 | template 253 | void smallestEigPositiveDefinite( SparseMatrix& A, 254 | SparseMatrix& B, 255 | DenseMatrix& E, 256 | DenseMatrix& x ); 257 | // solves A x = lambda (B - EE^T) x for the smallest nonzero eigenvalue lambda 258 | // A must be positive (semi-)definite, B must be symmetric; EE^T is a low-rank matrix, and 259 | // x is used as an initial guess 260 | 261 | template 262 | double residual( const SparseMatrix& A, 263 | const DenseMatrix& x, 264 | const DenseMatrix& b ); 265 | // returns the max residual of the linear problem A x = b relative to the largest entry of the solution 266 | 267 | template 268 | double residual( const SparseMatrix& A, 269 | const DenseMatrix& x ); 270 | // returns the max residual of the eigenvalue problem A x = lambda x relative to the largest entry of the solution 271 | 272 | template 273 | double residual( const SparseMatrix& A, 274 | const SparseMatrix& B, 275 | const DenseMatrix& x ); 276 | // returns the max residual of the generalized eigenvalue problem A x = lambda B x relative to the largest entry of the solution 277 | 278 | template 279 | double residual( const SparseMatrix& A, 280 | const SparseMatrix& B, 281 | const DenseMatrix& E, 282 | const DenseMatrix& x ); 283 | // returns the max residual of the generalized eigenvalue problem A x = lambda (B - EE^T) x relative to the largest entry of the solution 284 | 285 | template 286 | T rayleighQuotient( const SparseMatrix& A, 287 | const DenseMatrix& x ); 288 | // returns / 289 | 290 | template 291 | T rayleighQuotient( SparseMatrix& A, 292 | SparseMatrix& B, 293 | DenseMatrix& x ); 294 | // returns / 295 | 296 | template 297 | T rayleighQuotient( const SparseMatrix& A, 298 | const SparseMatrix& B, 299 | const DenseMatrix& E, 300 | const DenseMatrix& x ); 301 | // returns /<(B-EE^T)x,x> 302 | 303 | class ColumnEntry 304 | { 305 | public: 306 | size_t index; 307 | Complex value; 308 | 309 | ColumnEntry( void ) {} 310 | ColumnEntry( size_t i, Complex z ) 311 | : index( i ), value( z ) 312 | {} 313 | 314 | bool operator<( const ColumnEntry& e ) const 315 | { 316 | return index < e.index; 317 | } 318 | }; 319 | } 320 | 321 | #include "SparseMatrix.inl" 322 | 323 | #endif 324 | -------------------------------------------------------------------------------- /src/KVecDir.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Utility.h" 7 | #include "Complex.h" 8 | #include "Mesh.h" 9 | #include "SparseMatrix.h" 10 | #include "SectionIntegrals.h" 11 | 12 | namespace DDG{ 13 | 14 | // turn angle into unitary complex number 15 | inline const Complex Phase( const double a ) { return Complex(cos(a),sin(a)); } 16 | 17 | // angle from f to t in the plane orthogonal to N 18 | inline const double Angle( const Vector& f, const Vector& t, const Vector& N ){ 19 | return atan2( dot(cross(f, t), N), dot(f, t) ); 20 | } 21 | 22 | inline const double Clip( const double value, const double lower, const double upper ){ 23 | return value < lower ? lower : ( value > upper ? upper : value ); 24 | } 25 | 26 | void Mesh::ComputeConnectionAndHopf( void ){ 27 | // at this point all face->alpha[] slots contain the Euclidean tip angles 28 | // now visit every edge and figure out transport along that edge 29 | for( EdgeIter ei = edges.begin(); ei != edges.end(); ei++ ){ 30 | const VertexIter vi = ei->he->vertex, vj = ei->he->flip->vertex; 31 | 32 | // AngleOfEdge assumes that the alpha[] slots in all triangles 33 | // contain the Euclidean angles; it then returns the angle 34 | // measured from the distinguished direciton to the current edge 35 | // RESCALED (by s). So it is the flattened angle that is 36 | // measured here 37 | const double aCi = vi->AngleOfEdge( ei->he ); 38 | // we subtract PI from this one since we don't want to know the 39 | // angle to ei->he->flip, but rather ei->he; undo the additional 40 | // PI rotation 41 | const double aCj = vj->AngleOfEdge( ei->he->flip ) - DDGConstants::PI; 42 | 43 | // what coefficient do I need to express X_i using the frame of X_j: X_i = r_{ij} X_j 44 | ei->r = ( aCj - aCi ); 45 | // w holds possible modification of connection; initially: none 46 | ei->w = 0; // REMOVE w 47 | 48 | // let's compute q on this edge 49 | const double le = ei->he->geom().norm(); 50 | if( ei->he->onBoundary || ei->he->flip->onBoundary ){ 51 | // boundary edges have a zero dihedral angle 52 | ei->q = 0; 53 | }else{ 54 | // need Clip() here to avoid being ever so slightly larger than 1 or smaller than -1 55 | // const double cs = Clip( dot(ei->he->face->normal, ei->he->flip->face->normal ), -1, 1 ); 56 | // in the edge tangent space (distinguished direction is along edge) q is purely real 57 | // ei->q = -Complex(acos(cs)/2*le,0); 58 | ei->q = -Complex(Angle( ei->he->face->normal, ei->he->flip->face->normal, ei->he->geom().unit() )/2*le,0); 59 | // just making sure... 60 | assert( !isnan(ei->q.re) ); 61 | } 62 | 63 | // assuming that the q is aligned with the edge it gets 64 | // transported into each end by the angles at the vertices with 65 | // the edge; since q is quadratic we use the square 66 | vj->q += Phase(aCj*2) * ei->q; 67 | vi->q += Phase(aCi*2) * ei->q; 68 | } // edge traversal 69 | } 70 | 71 | // new version which uses closed form integrals 72 | void Mesh::InitKVecDirData( void ){ 73 | 74 | // we'll need transports along edges, areas (masses) of triangles 75 | // and masses (areas) of vertices; the latter is needed for the EV 76 | // problem as the mass matrix from the rhs gets incorporated into 77 | // the lhs so that we have just an ordinary EV problem 78 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); vi++ ){ 79 | // initializations: mass, angle sums, Hopf differential 80 | vi->m = 0; vi->s = 0; vi->q = Complex(0,0); 81 | } 82 | 83 | for( FaceIter fi = faces.begin(); fi != faces.end(); fi++ ){ 84 | if( fi->isBoundary() ) continue; // skip boundary face(s) 85 | // computes triangle area; puts Euclidean corner angles into 86 | // alpha[], adds them to the s accumulators in the three 87 | // vertices 88 | fi->InitAreaAnglesAngleSums(); 89 | } 90 | 91 | // we used s to accumulate the total angle around a vertex; turn 92 | // it into the scale factor we need; on the boundary no flattening 93 | // is performed 94 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); vi++ ){ 95 | vi->s = vi->onBoundary() ? 1 : (2*DDGConstants::PI / vi->s); 96 | } 97 | 98 | // assumes alpha angles and s scalings at vertices 99 | ComputeConnectionAndHopf(); 100 | } 101 | 102 | void Mesh::ComputeEnergyAndMass( const unsigned int n, const double s ){ 103 | 104 | // gotta zero out all working slots 105 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); vi++ ){ 106 | vi->m = 0; vi->Es = 0; 107 | } 108 | for( EdgeIter ei = edges.begin(); ei != edges.end(); ei++ ){ 109 | ei->m = Complex(0,0); ei->Es = Complex(0,0); 110 | } 111 | 112 | // each local stiffness matrix 113 | for( FaceIter fi = faces.begin(); fi != faces.end(); fi++ ){ 114 | 115 | if( fi->isBoundary() ) continue; // nothing to do for boundary loop 116 | assert( fi->he->next->next->next == fi->he ); // better be a triangle 117 | 118 | // boundary of triangle ij, jk, ki 119 | const HalfEdgeIter he[3] = { fi->he, fi->he->next, fi->he->next->next }; 120 | // get vertices for this triangle 121 | const VertexIter v[3] = { he[0]->vertex, he[1]->vertex, he[2]->vertex }; 122 | // get edge differences, ij, jk, ki 123 | const Vector p[3] = { he[0]->geom(), he[1]->geom(), he[2]->geom() }; 124 | // length squared 125 | const double pn2[3] = { p[0].norm2(), p[1].norm2(), p[2].norm2() }; 126 | 127 | // transport coefficients along the edges (this is where 128 | // multiplication with k enters); we need to account for the 129 | // orientation of the halfedge relative to the edge; since we 130 | // only need the conjugate of these we'll conjugate right here 131 | const Complex rc[3] = // ADD w for flat bundle 132 | { Phase(n*(he[0]->r()/*+he[0]->w()*/)).conj(), 133 | Phase(n*(he[1]->r()/*+he[1]->w()*/)).conj(), 134 | Phase(n*(he[2]->r()/*+he[2]->w()*/)).conj() }; 135 | 136 | // area of this triangle 137 | const double A = fi->A; 138 | // line bundle curvature of this triangle 139 | const double om = n*(fi->K()/*+(he[0]->w()+he[1]->w()+he[2]->w())*/); // ADD w for flat line bundle 140 | 141 | // first the mass matrix 142 | const double Mii = A*MassII(); 143 | // diagonal entries at vertices 144 | v[0]->m += Mii; v[1]->m += Mii; v[2]->m += Mii; 145 | 146 | const Complex Mij = A*MassIJ( om ); 147 | // off diagonal at edges 148 | he[0]->mAcc( Mij*rc[0] ); he[1]->mAcc( Mij*rc[1] ); he[2]->mAcc( Mij*rc[2] ); 149 | 150 | // energy matrix 151 | const double snKii = (s*(om/A))*Mii; assert( !isnan( snKii ) ); 152 | // diagonal entries 153 | v[0]->Es += DirichletII( om, pn2[0], -dot(p[0],p[2]), pn2[2] )/A - snKii; 154 | v[1]->Es += DirichletII( om, pn2[1], -dot(p[1],p[0]), pn2[0] )/A - snKii; 155 | v[2]->Es += DirichletII( om, pn2[2], -dot(p[2],p[1]), pn2[1] )/A - snKii; 156 | 157 | const Complex snKij = s*((om/A)*Mij-Complex(0,.5)); 158 | // off diagonal 159 | he[0]->EsAcc( (DirichletIJ( om, pn2[2], -dot(p[2],p[1]), pn2[1] )/A - snKij)*rc[0] ); 160 | he[1]->EsAcc( (DirichletIJ( om, pn2[0], -dot(p[0],p[2]), pn2[2] )/A - snKij)*rc[1] ); 161 | he[2]->EsAcc( (DirichletIJ( om, pn2[1], -dot(p[1],p[0]), pn2[0] )/A - snKij)*rc[2] ); 162 | } // Face traversal 163 | } 164 | 165 | void Mesh::SetupEnergyMatrix( SparseMatrix &A, 166 | SparseMatrix &M, 167 | const unsigned int n, const double s, 168 | double lambda ) 169 | { 170 | const unsigned int nV = vertices.size(); 171 | const unsigned int nE = edges.size(); 172 | A.resize( nV, nV, nV+2*nE ); 173 | M.resize( nV, nV, nV+2*nE ); 174 | 175 | ComputeEnergyAndMass( n, s ); 176 | 177 | // shift A by epsilon to avoid degeneracy 178 | // (does not affect smallest eigenvector) 179 | const double shift = -lambda + 1e-9; 180 | 181 | // temporary storage for matrix columns 182 | vector< vector > cA( nV ), cM( nV ); 183 | 184 | for( VertexIter vj = vertices.begin(); vj != vertices.end(); ++vj ) 185 | { 186 | size_t j = vj->id; 187 | 188 | vector& Aj( cA[j] ); // nonzeros in jth column of A 189 | vector& Mj( cM[j] ); // nonzeros in jth column of M 190 | Aj.reserve( 16 ); 191 | Mj.reserve( 16 ); 192 | 193 | Aj.push_back( ColumnEntry( j, Complex(vj->Es+shift*vj->m,0) )); 194 | Mj.push_back( ColumnEntry( j, Complex(vj->m,0) )); 195 | 196 | HalfEdgeCIter he = vj->he; 197 | do { 198 | EdgeCIter e = he->edge; 199 | VertexIter vi = he->flip->vertex; 200 | size_t i = vi->id; 201 | 202 | Complex Aij = e->Es + shift*e->m; 203 | Complex Mij = e->m; 204 | if( he->edge->he == he ){ 205 | Aij = Aij.conj(); 206 | Mij = Mij.conj(); 207 | } 208 | 209 | Aj.push_back( ColumnEntry( i, Aij )); 210 | Mj.push_back( ColumnEntry( i, Mij )); 211 | 212 | he = he->flip->next; 213 | } while( he != vj->he ); 214 | } 215 | 216 | for( int j = 0; j < nV; j++ ) 217 | { 218 | A.appendCompressedColumn( cA[j] ); 219 | M.appendCompressedColumn( cM[j] ); 220 | } 221 | } 222 | 223 | void Mesh::SetupEnergyMatrixFixedBoundary( SparseMatrix &A, 224 | SparseMatrix &M, 225 | DenseMatrix &b, 226 | const unsigned int n, const double s, 227 | double lambda ) 228 | { 229 | const unsigned int nV = nInteriorVertices; 230 | const unsigned int nE = edges.size(); 231 | A.resize( nV, nV, nV+2*nE ); 232 | M.resize( nV, nV, nV+2*nE ); 233 | b = DenseMatrix( nV, 1 ); 234 | b.zero( Complex(0.,0.) ); 235 | 236 | ComputeEnergyAndMass( n, s ); 237 | 238 | // shift A by epsilon to avoid degeneracy 239 | // (does not affect smallest eigenvector) 240 | const double shift = -lambda + 1e-9; 241 | 242 | // temporary storage for matrix columns 243 | vector< vector > cA( nV ), cM( nV ); 244 | 245 | for( VertexIter vj = vertices.begin(); vj != vertices.end(); ++vj ) 246 | { 247 | if( vj->onBoundary() ) continue; 248 | 249 | size_t j = vj->id; 250 | 251 | vector& Aj( cA[j] ); // nonzeros in jth column of A 252 | vector& Mj( cM[j] ); // nonzeros in jth column of M 253 | Aj.reserve( 16 ); 254 | Mj.reserve( 16 ); 255 | 256 | Aj.push_back( ColumnEntry( j, Complex(vj->Es+shift*vj->m,0) )); 257 | Mj.push_back( ColumnEntry( j, Complex(vj->m,0) )); 258 | 259 | HalfEdgeCIter he = vj->he; 260 | do { 261 | EdgeCIter e = he->edge; 262 | VertexIter vi = he->flip->vertex; 263 | 264 | Complex Aij = e->Es + shift*e->m; 265 | Complex Mij = e->m; 266 | if( he->edge->he == he ){ 267 | Aij = Aij.conj(); 268 | Mij = Mij.conj(); 269 | } 270 | 271 | if( vi->onBoundary() ) 272 | { 273 | // move boundary terms to the right-hand side 274 | b(j,0) -= Aij.conj() * vi->BoundaryValue(n); 275 | } 276 | else 277 | { 278 | size_t i = vi->id; 279 | Aj.push_back( ColumnEntry( i, Aij )); 280 | Mj.push_back( ColumnEntry( i, Mij )); 281 | } 282 | 283 | he = he->flip->next; 284 | } while( he != vj->he ); 285 | } 286 | 287 | for( int j = 0; j < nV; j++ ) 288 | { 289 | A.appendCompressedColumn( cA[j] ); 290 | M.appendCompressedColumn( cM[j] ); 291 | } 292 | } 293 | 294 | // energy minimizer 295 | void Mesh::ComputeSmoothest( const unsigned int n, const double s, const bool dir ){ 296 | 297 | cerr << "Mesh::ComputeSmoothest: n: " << n << " s: " << s << " dir: " << dir << endl; 298 | 299 | double t0 = wallClock(); 300 | 301 | const unsigned int nv = vertices.size(); 302 | SparseMatrix A( nv, nv ), M( nv, nv ); 303 | 304 | SetupEnergyMatrix( A, M, n, s ); 305 | 306 | // now find smallest eigenvector 307 | DenseMatrix u(nv,1); 308 | u.randomize(); 309 | 310 | smallestEigPositiveDefinite( A, M, u ); 311 | cerr << "[eig] ev = " << rayleighQuotient( A, M, u ) << endl; 312 | 313 | // load result 314 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); vi++ ) 315 | { 316 | vi->u = u(vi->id,0); 317 | } 318 | 319 | ComputeIndices( n ); 320 | 321 | // take roots (and possibly normalize) vertex data 322 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); vi++ ){ 323 | const Complex z = vi->u; 324 | vi->u = Phase(z.arg()/n) * ( dir ? 1. : pow(z.norm(),1./n) ); 325 | } 326 | double t1 = wallClock(); printTiming( "compute smoothest field", t1-t0 ); 327 | cerr << "triangles: " << faces.size() << endl; 328 | } // ComputeSmoothest 329 | 330 | void Mesh::ComputeSmoothestFixedBoundary( const unsigned int n, const double s, const bool dir ){ 331 | 332 | cerr << "Mesh::ComputeSmoothestFixedBoundary: n: " << n << " s: " << s << " dir: " << dir << endl; 333 | 334 | double t0 = wallClock(); 335 | 336 | const unsigned int nv = nInteriorVertices; 337 | SparseMatrix A( nv, nv ), M( nv, nv ); 338 | DenseMatrix b; 339 | 340 | cout << "Build matrix" << endl; 341 | SetupEnergyMatrixFixedBoundary( A, M, b, n, s ); 342 | 343 | cout << "Solve" << endl; 344 | // solve for smoothest field 345 | DenseMatrix u(nv,1); 346 | solvePositiveDefinite( A, u, b ); 347 | 348 | cerr << "Extract solution" << endl; 349 | // load result 350 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); vi++ ) 351 | { 352 | if( !vi->onBoundary() ) // interior vertex 353 | { 354 | vi->u = u(vi->id,0); 355 | } 356 | else // boundary vertex 357 | { 358 | vi->u = vi->BoundaryValue(n); 359 | } 360 | } 361 | 362 | cout << "Compute indices" << endl; 363 | ComputeIndices( n ); 364 | 365 | // take roots (and possibly normalize) vertex data 366 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); vi++ ){ 367 | const Complex z = vi->u; 368 | vi->u = Phase(z.arg()/n) * ( dir ? 1. : pow(z.norm(),1./n) ); 369 | } 370 | double t1 = wallClock(); printTiming( "compute smoothest field", t1-t0 ); 371 | cerr << "triangles: " << faces.size() << endl; 372 | } // ComputeSmoothest 373 | 374 | // alignment with q slot at vertices code; typically q is the Hopf 375 | // differential, but could be something else; here we will assume 376 | // however that it is the Hopf differential 377 | double Mesh::SmoothestCurvatureAlignment( unsigned int n, double s, double lambda, bool dir ){ 378 | cerr << "Mesh::CurvatureSmoothestAlignment: n: " << n << " s: " << s << " lambda: " << lambda << endl; 379 | if( n != 2 && n != 4 ){ 380 | cerr << "alignment code requires n == 2 or n == 4; n is: " << n << endl; 381 | return 0; 382 | } 383 | double t0 = wallClock(); 384 | 385 | const unsigned int nv = vertices.size(); 386 | SparseMatrix A( nv, nv ), M( nv, nv ); 387 | 388 | SetupEnergyMatrix( A, M, n, s, lambda ); 389 | 390 | // find solution to Poisson problem 391 | DenseMatrix u(nv,1), q(nv,1); 392 | 393 | // load q; to simplify the t computation we need to normalize q 394 | unsigned int i = 0; 395 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); i++, vi++ ){ 396 | q(vi->id,0) = ( n == 2 ? vi->q : vi->q*vi->q ); 397 | } 398 | { 399 | u = M.multiply( q ); 400 | double normQ = 0; 401 | for( i = 0; i < nv; i++ ) normQ += (q(i,0).conj()*u(i,0)).re; 402 | q = u/sqrt( normQ ); 403 | } 404 | 405 | solvePositiveDefinite( A, u, q ); 406 | 407 | // load result 408 | i = 0; for( VertexIter vi = vertices.begin(); vi != vertices.end(); i++, vi++ ) vi->u = u(vi->id,0); 409 | 410 | double normU = 0; 411 | { 412 | q = M*u; 413 | for( i = 0; i < nv; i++ ){ 414 | normU += (u(i,0).conj()*q(i,0)).re; 415 | } 416 | normU = sqrt( normU ); 417 | } 418 | 419 | ComputeIndices( n ); 420 | 421 | // take roots (and possibly normalize) vertex data 422 | for( VertexIter vi = vertices.begin(); vi != vertices.end(); vi++ ){ 423 | const Complex z = vi->u; 424 | vi->u = Phase(z.arg()/n) * ( dir ? 1. : pow(z.norm(),1./n) ); 425 | } 426 | double t1 = wallClock(); printTiming( "compute aligned field", t1-t0 ); 427 | cerr << "triangles: " << faces.size() << endl; 428 | 429 | // let the caller know what the actual t value was 430 | cerr << "corresponding t value: " << 1./(1.+normU) << endl; 431 | return 1./(1.+normU); 432 | } 433 | 434 | // determine singularity index of each triangle 435 | void Mesh::ComputeIndices( const unsigned int n ) { 436 | 437 | int totalIndex = 0; 438 | for( FaceIter fi = faces.begin(); fi != faces.end(); fi++ ){ 439 | if( fi->isBoundary() ) continue; 440 | 441 | // boundary of triangle ij, jk, ki 442 | const HalfEdgeCIter he[3] = { fi->he, fi->he->next, fi->he->next->next }; 443 | 444 | // index computation; first the geometric curvature of this 445 | // triangle 446 | const double sigma = fmodPI(n*fi->K()/*+n*(he[0]->w()+he[1]->w()+he[2]->w())*/); // ADD w for flat bundle 447 | 448 | // now walk around the boundary and look by how much the 449 | // vector field rotates. 450 | const Complex u[3] = { he[0]->vertex->u, he[1]->vertex->u, he[2]->vertex->u }; 451 | 452 | // transport from vertex i to vertex j. 453 | const double rij[3] = // ADD w for flat bundle 454 | { fmodPI(n*(he[0]->r()/*+he[0]->w()*/)), 455 | fmodPI(n*(he[1]->r()/*+he[1]->w()*/)), 456 | fmodPI(n*(he[2]->r()/*+he[2]->w()*/)) }; 457 | 458 | // now figure out how much the u field rotates all by itself; 459 | // transport a u_i to v_j and compare with u_j there 460 | const double aij[3] = { 461 | ( u[0].unit() * Phase( rij[0] ) * u[1].unit().conj() ).arg(), 462 | ( u[1].unit() * Phase( rij[1] ) * u[2].unit().conj() ).arg(), 463 | ( u[2].unit() * Phase( rij[2] ) * u[0].unit().conj() ).arg() }; 464 | 465 | //assert( abs( aij[0] + aij[1] + aij[2] - sigma ) < 4*DDGConstants::PI ); 466 | 467 | // final result (note that we have an overall "sign error" since 468 | // we measured angles in CW orientation) 469 | const double s = -( aij[0] + aij[1] + aij[2] - sigma ) / (2*DDGConstants::PI ); 470 | 471 | // this better be essentially an integer 472 | assert( abs( s - lround( s ) ) < 1e-6 ); 473 | 474 | fi->sing = int( lround( s ) ); 475 | 476 | totalIndex += fi->sing; 477 | 478 | } // Face traversal 479 | } // ComputeIndices 480 | 481 | void Mesh::clearSingularities( void ) 482 | { 483 | for( FaceIter f = faces.begin(); f != faces.end(); f++ ) 484 | { 485 | f->sing = 0; 486 | } 487 | } 488 | 489 | } // namespace DDG 490 | -------------------------------------------------------------------------------- /src/MeshIO.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "MeshIO.h" 8 | #include "Mesh.h" 9 | 10 | using namespace std; 11 | 12 | namespace DDG 13 | { 14 | int MeshIO :: read( istream& in, Mesh& mesh ) 15 | // reads a mesh from a valid, open input stream in 16 | { 17 | MeshData data; 18 | 19 | if( readMeshData( in, data )) 20 | { 21 | return 1; 22 | } 23 | 24 | if( buildMesh( data, mesh )) 25 | { 26 | return 1; 27 | } 28 | 29 | return 0; 30 | } 31 | 32 | void MeshIO :: write( ostream& out, const Mesh& mesh, unsigned int n ) 33 | // writes a mesh to a valid, open output stream out 34 | { 35 | out << "# out.obj" << endl; 36 | out << "#" << endl; 37 | out << "# This file contains a triangle mesh in Wavefront OBJ format." << endl; 38 | out << "# It also includes a tangent vector field, encoded in comment" << endl; 39 | out << "# lines at the end of the file. The degree of the field is" << endl; 40 | out << "# specified by a line of the form" << endl; 41 | out << "#" << endl; 42 | out << "# degree n" << endl; 43 | out << "#" << endl; 44 | out << "# where (for instance) n=1 is a unit vector field, n=2 is a" << endl; 45 | out << "# line field, and n=4 is a cross field. Individual vectors" << endl; 46 | out << "# are then specified by lines of the form" << endl; 47 | out << "#" << endl; 48 | out << "# field i x y z" << endl; 49 | out << "#" << endl; 50 | out << "# where i is the index of the vertex, and x y z are the three" << endl; 51 | out << "# components of the tangent vector. In the case where these" << endl; 52 | out << "# vectors encode an n-direction field this vector is just one" << endl; 53 | out << "# of the n possible vectors. The other vectors can be obtained" << endl; 54 | out << "# by rotating this one around the corresponding vertex normal," << endl; 55 | out << "# which is given in the usual vn line. Singularities in the" << endl; 56 | out << "# field, which are associated with faces, are indicated by lines" << endl; 57 | out << "#" << endl; 58 | out << "# singularity i s" << endl; 59 | out << "#" << endl; 60 | out << "# where i is the index of the triangle, and s is the degree of" << endl; 61 | out << "# the singularity. All indices are 1-based rather than 0-based." << endl; 62 | out << "#" << endl; 63 | out << "# This field was generated using the fieldgen program:" << endl; 64 | out << "#" << endl; 65 | out << "# https://github.com/GeometryCollective/fieldgen" << endl; 66 | out << "#" << endl; 67 | out << endl; 68 | 69 | int currentIndex = 1; 70 | map vertexIndex; 71 | 72 | for( VertexCIter v = mesh.vertices.begin(); 73 | v != mesh.vertices.end(); 74 | v++ ) 75 | { 76 | out << "v " << v->position.x << " " 77 | << v->position.y << " " 78 | << v->position.z << endl; 79 | 80 | vertexIndex[ v ] = currentIndex; 81 | currentIndex++; 82 | } 83 | 84 | out << "vt 0 0" << endl; 85 | 86 | for( VertexCIter v = mesh.vertices.begin(); 87 | v != mesh.vertices.end(); 88 | v++ ) 89 | { 90 | Vector N = v->normal; 91 | out << "vn " << N.x << " " 92 | << N.y << " " 93 | << N.z << endl; 94 | } 95 | 96 | for( size_t i = 0; i < mesh.faces.size(); i++ ) 97 | { 98 | const Face& f( mesh.faces[i] ); 99 | HalfEdgeIter he = f.he; 100 | 101 | // don't write boundary faces 102 | if( he->onBoundary ) 103 | { 104 | continue; 105 | } 106 | 107 | out << "f "; 108 | 109 | do 110 | { 111 | int j = vertexIndex[ he->vertex ]; 112 | out << j << "/1/" << j << " "; 113 | he = he->next; 114 | } 115 | while( he != f.he ); 116 | 117 | out << endl; 118 | } 119 | 120 | out << "# degree " << n << endl; 121 | 122 | for( VertexCIter v = mesh.vertices.begin(); v != mesh.vertices.end(); v++ ) 123 | { 124 | const Vector c = v->position; // vertex location 125 | const Vector N = v->normal; // normal 126 | const Vector e1 = v->Xvector().unit(); // bases for tangent plane 127 | const Vector e2 = cross( N, e1 ); 128 | 129 | const double theta = v->u.arg(); 130 | const Vector X = cos(theta)*e1 + sin(theta)*e2; 131 | 132 | int i = vertexIndex[v]; 133 | out << "# field " << i << " " << X.x << " " << X.y << " " << X.z << endl; 134 | } 135 | 136 | int p = 1; 137 | for( FaceCIter f = mesh.faces.begin(); 138 | f != mesh.faces.end(); 139 | f ++ ) 140 | { 141 | if( f->isBoundary() ) continue; 142 | if( f->sing != 0 ) 143 | { 144 | out << "# singularity " << p << " " << (double)f->sing/(double)n << endl; 145 | } 146 | p++; 147 | } 148 | 149 | } 150 | 151 | int MeshIO :: readMeshData( istream& in, MeshData& data ) 152 | { 153 | string line; 154 | 155 | while( getline( in, line )) 156 | { 157 | stringstream ss( line ); 158 | string token; 159 | 160 | ss >> token; 161 | 162 | if( token == "v" ) { readPosition( ss, data ); continue; } // vertex 163 | if( token == "vt" ) { readTexCoord( ss, data ); continue; } // texture coordinate 164 | if( token == "vn" ) { readNormal ( ss, data ); continue; } // vertex normal 165 | if( token == "f" ) { readFace ( ss, data ); continue; } // face 166 | if( token[0] == '#' ) continue; // comment 167 | if( token == "o" ) continue; // object name 168 | if( token == "g" ) continue; // group name 169 | if( token == "s" ) continue; // smoothing group 170 | if( token == "mtllib" ) continue; // material library 171 | if( token == "usemtl" ) continue; // material 172 | if( token == "" ) continue; // empty string 173 | 174 | cerr << "Error: does not appear to be a valid Wavefront OBJ file!" << endl; 175 | cerr << "(Offending line: " << line << ")" << endl; 176 | return 1; 177 | } 178 | 179 | return 0; 180 | } 181 | 182 | void MeshIO :: preallocateMeshElements( const MeshData& data, Mesh& mesh ) 183 | { 184 | // count the number of edges 185 | set< pair > edges; 186 | for( vector< vector< Index > >::const_iterator f = data.indices.begin(); 187 | f != data.indices.end(); 188 | f ++ ) 189 | { 190 | for( unsigned int I = 0; I < f->size(); I++ ) 191 | { 192 | int J = (I+1) % f->size(); 193 | int i = (*f)[I].position; 194 | int j = (*f)[J].position; 195 | 196 | if( i > j ) swap( i, j ); 197 | 198 | edges.insert( pair( i, j )); 199 | } 200 | } 201 | 202 | int nV = data.positions.size(); 203 | int nE = edges.size(); 204 | int nF = data.indices.size(); 205 | int nHE = 2*nE; 206 | int chi = nV - nE + nF; 207 | int nB = max( 0, 2 - chi ); // (conservative approximation of number of boundary cycles) 208 | 209 | mesh.halfedges.clear(); 210 | mesh.vertices.clear(); 211 | mesh.edges.clear(); 212 | mesh.faces.clear(); 213 | 214 | mesh.halfedges.reserve( nHE ); 215 | mesh.vertices.reserve( nV ); 216 | mesh.edges.reserve( nE ); 217 | mesh.faces.reserve( nF + nB ); 218 | } 219 | 220 | extern vector isolated; // all isolated vertices point to isolated.begin() 221 | 222 | int MeshIO :: buildMesh( const MeshData& data, Mesh& mesh ) 223 | { 224 | map< pair< int, int >, int > edgeCount; 225 | map< pair< int, int >, HalfEdgeIter > existingHalfEdges; 226 | map< int, VertexIter > indexToVertex; 227 | map< HalfEdgeIter, bool > hasFlipEdge; 228 | 229 | preallocateMeshElements( data, mesh ); 230 | 231 | // allocate a vertex for each position in the data and construct 232 | // a map from vertex indices to vertex pointers 233 | for( unsigned int i = 0; i < data.positions.size(); i++ ) 234 | { 235 | VertexIter newVertex = mesh.vertices.insert( mesh.vertices.end(), Vertex() ); 236 | newVertex->position = data.positions[ i ]; 237 | newVertex->he = isolated.begin(); 238 | indexToVertex[ i ] = newVertex; 239 | } 240 | 241 | // insert each face into the mesh 242 | int faceIndex = 0; 243 | bool degenerateFaces = false; 244 | for( vector< vector< Index > >::const_iterator f = data.indices.begin(); 245 | f != data.indices.end(); 246 | f ++ ) 247 | { 248 | int N = f->size(); 249 | 250 | // print an error if the face is degenerate 251 | if( N < 3 ) 252 | { 253 | cerr << "Error: face " << faceIndex << " is degenerate (fewer than three vertices)!" << endl; 254 | degenerateFaces = true; 255 | continue; 256 | } 257 | 258 | // create a new face 259 | FaceIter newFace = mesh.faces.insert( mesh.faces.end(), Face()); 260 | 261 | // create a new half edge for each edge of the current face 262 | vector< HalfEdgeIter > hes( N ); 263 | for( int i = 0; i < N; i++ ) 264 | { 265 | hes[ i ] = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); 266 | } 267 | 268 | // initialize these new halfedges 269 | for( int i = 0; i < N; i++ ) 270 | { 271 | // the current halfedge goes from vertex a to vertex b 272 | int a = (*f)[ i ].position; 273 | int b = (*f)[ (i+1) % N ].position; 274 | 275 | // set current halfedge's attributes 276 | hes[ i ]->next = hes[ (i+1) % N ]; 277 | hes[ i ]->vertex = indexToVertex[ a ]; 278 | int t = (*f)[i].texcoord; 279 | if( t >= 0 ) hes[ i ]->texcoord = data.texcoords[ t ]; 280 | else hes[ i ]->texcoord = Vector( 0., 0., 0. ); 281 | hes[ i ]->onBoundary = false; 282 | 283 | // keep track of which halfedges have flip edges defined (for detecting boundaries) 284 | hasFlipEdge[ hes[ i ]] = false; 285 | 286 | // point vertex a at the current halfedge 287 | indexToVertex[ a ]->he = hes[ i ]; 288 | 289 | // point the new face and this half edge to each-other 290 | hes[ i ]->face = newFace; 291 | newFace->he = hes[ i ]; 292 | 293 | // if we've created an edge between a and b in the past, it is the 294 | // flip edge of the current halfedge 295 | if( a > b ) swap( a, b ); 296 | if( existingHalfEdges.find( pair( a, b )) != existingHalfEdges.end()) 297 | { 298 | hes[ i ]->flip = existingHalfEdges[ pair( a, b ) ]; 299 | hes[ i ]->flip->flip = hes[ i ]; 300 | hes[ i ]->edge = hes[ i ]->flip->edge; 301 | hasFlipEdge[ hes[ i ]] = true; 302 | hasFlipEdge[ hes[ i ]->flip ] = true; 303 | } 304 | else // otherwise, create an edge connected to the current halfedge 305 | { 306 | hes[ i ]->edge = mesh.edges.insert( mesh.edges.end(), Edge()); 307 | hes[ i ]->edge->he = hes[i]; 308 | edgeCount[ pair( a, b ) ] = 0; 309 | } 310 | 311 | // record the fact that we've created a halfedge from a to b 312 | existingHalfEdges[ pair( a, b ) ] = hes[ i ]; 313 | 314 | // check for nonmanifold edges 315 | edgeCount[ pair( a, b ) ]++; 316 | if( edgeCount[ pair( a, b ) ] > 2 ) 317 | { 318 | cerr << "Error: edge (" << a << ", " << b << ") is nonmanifold (more than two faces sharing a single edge)!" << endl; 319 | return 1; 320 | } 321 | } 322 | 323 | faceIndex++; 324 | } 325 | 326 | // give up now if there were degenerate faces 327 | if( degenerateFaces ) 328 | { 329 | return 1; 330 | } 331 | 332 | // insert extra faces for each boundary cycle 333 | for( HalfEdgeIter currentHE = mesh.halfedges.begin(); 334 | currentHE != mesh.halfedges.end(); 335 | currentHE ++ ) 336 | { 337 | // if we find a halfedge with no flip edge defined, create 338 | // a new face and link it to the corresponding boundary cycle 339 | 340 | if( !hasFlipEdge[ currentHE ] ) 341 | { 342 | // create a new face 343 | FaceIter newFace = mesh.faces.insert( mesh.faces.end(), Face()); 344 | 345 | // walk along this boundary cycle 346 | vector boundaryCycle; 347 | HalfEdgeIter he = currentHE; 348 | do 349 | { 350 | // create a new halfedge on the boundary face 351 | HalfEdgeIter newHE = mesh.halfedges.insert( mesh.halfedges.end(), HalfEdge()); 352 | 353 | // mark only the halfedge on the boundary face as being on the boundary 354 | newHE->onBoundary = true; 355 | 356 | // link the current halfedge in the cycle to its new flip edge 357 | he->flip = newHE; 358 | 359 | // grab the next halfedge along the boundary by finding 360 | // the next halfedge around the current vertex that doesn't 361 | // have a flip edge defined 362 | HalfEdgeIter nextHE = he->next; 363 | while( hasFlipEdge[ nextHE ] ) 364 | { 365 | nextHE = nextHE->flip->next; 366 | } 367 | 368 | // set attributes for the flip edge (we'll set ->next below) 369 | newHE->flip = he; 370 | newHE->vertex = nextHE->vertex; 371 | newHE->edge = he->edge; 372 | newHE->face = newFace; 373 | newHE->texcoord = nextHE->texcoord; 374 | 375 | // point the new face to this half edge 376 | newFace->he = newHE; 377 | 378 | // keep track of all the new halfedges in the boundary cycle 379 | boundaryCycle.push_back( newHE ); 380 | 381 | // continue to walk along the cycle 382 | he = nextHE; 383 | 384 | } while( he != currentHE ); 385 | 386 | // link together the cycle of boundary halfedges 387 | unsigned int N = boundaryCycle.size(); 388 | for( unsigned int i = 0; i < N; i++ ) 389 | { 390 | boundaryCycle[ i ]->next = boundaryCycle[ (i+N-1)%N ]; 391 | hasFlipEdge[ boundaryCycle[i] ] = true; 392 | hasFlipEdge[ boundaryCycle[i]->flip ] = true; 393 | } 394 | } 395 | } 396 | 397 | // print a warning if the input has any non-terminal defects 398 | checkIsolatedVertices( mesh ); 399 | checkNonManifoldVertices( mesh ); 400 | 401 | return 0; 402 | } 403 | 404 | void MeshIO :: readPosition( stringstream& ss, MeshData& data ) 405 | { 406 | double x, y, z; 407 | 408 | ss >> x >> y >> z; 409 | 410 | data.positions.push_back( Vector( x, y, z )); 411 | } 412 | 413 | void MeshIO :: readTexCoord( stringstream& ss, MeshData& data ) 414 | { 415 | double u, v; 416 | 417 | ss >> u >> v; 418 | 419 | data.texcoords.push_back( Vector( u, v, 0. )); 420 | } 421 | 422 | void MeshIO :: readNormal( stringstream& ss, MeshData& data ) 423 | { 424 | double x, y, z; 425 | 426 | ss >> x >> y >> z; 427 | 428 | data.normals.push_back( Vector( x, y, z )); 429 | } 430 | 431 | void MeshIO :: readFace( stringstream& ss, MeshData &data ) 432 | { 433 | vector faceIndices; 434 | string token; 435 | 436 | while( ss >> token ) 437 | { 438 | faceIndices.push_back( parseFaceIndex( token )); 439 | } 440 | 441 | data.indices.push_back( faceIndices ); 442 | } 443 | 444 | Index MeshIO :: parseFaceIndex( const string& token ) 445 | { 446 | // parse indices of the form 447 | // 448 | // p/[t]/[n] 449 | // 450 | // where p is an index into positions, t is an index into 451 | // texcoords, n is an index into normals, and [.] indicates 452 | // that an index is optional 453 | 454 | stringstream in( token ); 455 | string indexstring; 456 | int indices[3] = { -1, -1, -1 }; 457 | int i = 0; 458 | 459 | while( getline( in, indexstring, '/' )) 460 | { 461 | stringstream ss( indexstring ); 462 | ss >> indices[i++]; 463 | } 464 | 465 | // decrement since indices in OBJ files are 1-based 466 | return Index( indices[0]-1, 467 | indices[1]-1, 468 | indices[2]-1 ); 469 | } 470 | 471 | void MeshIO :: checkIsolatedVertices( const Mesh& mesh ) 472 | { 473 | // print a warning if the mesh has any isolated vertices 474 | int vertexIndex = 0; 475 | for( VertexCIter v = mesh.vertices.begin(); 476 | v != mesh.vertices.end(); 477 | v ++ ) 478 | { 479 | if( v->isIsolated() ) 480 | { 481 | cerr << "Warning: vertex " << vertexIndex << " is isolated (not contained in any face)." << endl; 482 | } 483 | 484 | vertexIndex++; 485 | } 486 | } 487 | 488 | void MeshIO :: checkNonManifoldVertices( const Mesh& mesh ) 489 | { 490 | map nIncidentFaces; 491 | 492 | for( FaceCIter f = mesh.faces.begin(); 493 | f != mesh.faces.end(); 494 | f ++ ) 495 | { 496 | HalfEdgeCIter he = f->he; 497 | do 498 | { 499 | nIncidentFaces[he->vertex]++; 500 | he = he->next; 501 | } 502 | while( he != f->he ); 503 | } 504 | 505 | unsigned int vertexIndex = 0; 506 | for( VertexCIter v = mesh.vertices.begin(); 507 | v != mesh.vertices.end(); 508 | v ++ ) 509 | { 510 | if( nIncidentFaces[v] != v->valence() ) 511 | { 512 | cerr << "Warning: vertex " << vertexIndex << " is nonmanifold." << endl; 513 | } 514 | 515 | vertexIndex++; 516 | } 517 | } 518 | } 519 | 520 | --------------------------------------------------------------------------------