├── GLIDViewer ├── GLProgram.cpp ├── GLProgram.h ├── MyImage.h ├── VAOImage.h ├── deformer.h ├── gl_core_3_3.c ├── gl_core_3_3.h ├── glarray.h ├── glidviewer.sln ├── glidviewer.vcxproj ├── glidviewer.vcxproj.filters ├── glidviewer.vcxproj.user ├── main.cpp ├── matlabDeformer.h ├── matlab_utils.cpp ├── matlab_utils.h ├── stb_image.h ├── stb_image_resize.h ├── stb_image_write.h └── vaomesh.h ├── GLID_main.m ├── GetCompCage.m ├── README.md ├── cauchyCoordinates.m ├── cdt.m ├── cuHarmonic.mexw64 ├── cuHarmonic ├── cuHarmonic.sln ├── cuHarmonic.vcxproj ├── cuHarmonic.vcxproj.filters ├── cuHarmonicDeform.cuh ├── dummy.cpp ├── harmonic_map.cuh ├── mex.def ├── mexCUHarmonic.cu └── utils.cuh ├── data ├── Archery │ ├── data.mat │ └── image.png ├── annulus │ ├── data.mat │ └── image.png ├── colorbar │ ├── data.mat │ └── image.png ├── deer │ ├── data.mat │ └── image.png ├── dragon │ ├── data.mat │ └── image.png ├── elephant │ ├── data.mat │ └── image.png ├── frog │ ├── data.mat │ └── image.png ├── giraffe │ ├── data.mat │ └── image.png ├── horse │ ├── data.mat │ └── image.png ├── monkey │ ├── data.mat │ └── image.png ├── racoon │ ├── data.mat │ └── image.png ├── raptor │ ├── data.mat │ └── image.png └── rex │ ├── data.mat │ └── image.png ├── deformer_main.m ├── derivativesOfCauchyCoord.m ├── distancePointToSegment.m ├── glidviewer.exe ├── harmonicMapIsometryicEnergy.m ├── hasGPUComputing.m ├── lbfgs_iter.m ├── lineSearchLocallyInjectiveHarmonicMap.m ├── list_datasets.m ├── maxtForPhiPsy.m ├── maxtForPositiveArea.m ├── meshAQP.m ├── meshAQP ├── code │ ├── OptimProblem.m │ ├── OptimProblemArap.m │ ├── OptimProblemIsoDist.m │ ├── OptimSolver.m │ ├── OptimSolverAcclQuadProx.m │ ├── OptimSolverIterative.m │ ├── mathHelpers │ │ ├── SparseLU.m │ │ ├── colStack.m │ │ ├── getTensorTranspose.m │ │ ├── solveConstrainedLS.m │ │ └── spdiag.m │ ├── meshHelpers │ │ ├── boundaryFaces.m │ │ ├── computeInjectiveStepSize.m │ │ ├── computeTutte.m │ │ ├── getDistortionColormap.m │ │ └── indCoordsToLinearSystem.m │ └── visualizeSolversForExamples.m ├── mex │ ├── computeFunctionalIsoDistMex.cpp │ ├── computeFunctionalIsoDistMex.mexw64 │ ├── computeInjectiveStepSizeMex.cpp │ ├── computeInjectiveStepSizeMex.mexw64 │ ├── computeMeshTranformationCoeffsMex.cpp │ ├── computeMeshTranformationCoeffsMex.mexw64 │ ├── mexHelpers.cpp │ ├── projectRotationMex.cpp │ ├── projectRotationMex.mexw64 │ ├── projectRotationMexFast.cpp │ └── projectRotationMexFast.mexw64 └── toolbox │ ├── LargeScaleBD │ ├── SolverProjector.m │ ├── SolverProjectorBD.m │ ├── SolverProjectorModeEnum.m │ ├── computeMeshTranformationCoeffsMex.mexw64 │ └── projectBDMex.mexw64 │ └── toolbox_graph │ └── progressbar.m ├── meshARAP.m ├── meshSLIM.m ├── nlo_p2p_harmonic.m ├── p2p_harmonic.m ├── p2p_harmonic_prep.m ├── p2p_harmonic_savedata.m ├── pointInPolygon.m ├── polygonOffset.m ├── selfintersect.m ├── signedAreas.m ├── signedpolyarea.m ├── subdivPolyMat.m └── triangle.bin /GLIDViewer/GLProgram.h: -------------------------------------------------------------------------------- 1 | #ifndef GLPROGRAM_H 2 | #define GLPROGRAM_H 3 | 4 | #include 5 | #include 6 | 7 | #define OPENGL_VERSION 3 8 | 9 | class GLProgram 10 | { 11 | public: 12 | enum ShaderType { VERTEX_SHADER = 0, GEOMETRY_SHADER, FRAGMENT_SHADER, TESS_CONTROL_SHADER, TESS_EVALUATION_SHADER, COMPUTE_SHADER, NUM_SHADER_TYPE }; 13 | static const char *const ShaderTypeStrs[NUM_SHADER_TYPE]; 14 | 15 | private: 16 | unsigned int pProgram; 17 | unsigned int pShader[NUM_SHADER_TYPE]; 18 | std::string logString; 19 | bool rowmajor; // for setting matrix uniforms 20 | 21 | struct Uniform { unsigned int type; int location, size, stride; }; 22 | 23 | struct BlockUniform { unsigned int type; int offset, size, arrayStride; }; 24 | 25 | /// stores information for a block and its uniforms 26 | struct UniformBlock { 27 | /// size of the uniform block 28 | int size; 29 | /// buffer bound to the index point 30 | unsigned int buffer; 31 | /// binding index 32 | unsigned int bindingIndex; 33 | /// uniforms information 34 | std::map uniformOffsets; 35 | }; 36 | 37 | std::map < std::string, Uniform > pUniforms; 38 | std::map < std::string, UniformBlock > pBlocks; 39 | 40 | public: 41 | GLProgram(); 42 | ~GLProgram(); 43 | 44 | void reset(); 45 | 46 | bool compileShaderFromFile( const char * fileName, ShaderType type ); 47 | bool compileShaderFromString( const std::string & source, ShaderType type ); 48 | int compileAndLinkAllShadersFromString(const std::string &vtxShaderStr, const std::string &fragShaderStr, const std::string &geomShaderStr=""); 49 | int compileAndLinkAllShadersFromFile(const char * vertexShaderFile, const char * fragShaderFile, const char *geomShaderFile=NULL); 50 | 51 | std::string getShaderInfoLog(ShaderType type); 52 | std::string getProgramInfoLog(); 53 | std::string getAllInfoLog(); 54 | 55 | bool link(); 56 | bool validate(); 57 | void bind(); 58 | void unbind(); 59 | 60 | bool isbinded(); 61 | 62 | std::string log() const { return logString; } 63 | 64 | int getHandle() { return pProgram; } 65 | 66 | bool shaderCompiled(ShaderType); 67 | bool linked(); 68 | 69 | void cacheUniforms(); 70 | void cacheBlocks(); 71 | 72 | void setUniformPVOID(const char *, void *); 73 | 74 | // only work on OpenGL4 hardware 75 | #if OPENGL_VERSION >= 4 76 | void setUniformPVOID_GL4(const char *, void *); 77 | #endif 78 | 79 | void setBlock(const char *, void *); 80 | void setBlockUniform(const char*, const char*, void *); 81 | void setBlockUniformArrayElement(const char *, const char *, int, void *); 82 | 83 | template 84 | typename std::enable_if::value&&std::is_scalar::value,void>::type setUniform(const char *name, R v1, Rs... v) { 85 | static_assert(sizeof(R) == 4 || sizeof(R) == 8, "OpenGL only takes data of unsigned, int, float, double"); 86 | R vals[] = { v1, v... }; 87 | setUniformPVOID(name, (void*)vals); 88 | } 89 | 90 | template 91 | typename std::enable_if::value,void>::type setUniform(const char *name, R* vals) { 92 | static_assert(sizeof(R) == 4 || sizeof(R) == 8, "OpenGL only takes data of unsigned, int, float, double"); 93 | setUniformPVOID(name, (void*)vals); 94 | } 95 | 96 | void bindAttribLocation(unsigned int location, const char * name); 97 | void bindFragDataLocation( unsigned int location, const char * name ); 98 | 99 | int getAttribLocation(const char * name); 100 | 101 | void printActiveUniforms(); 102 | void printActiveAttribs(); 103 | }; 104 | 105 | #endif // GLPROGRAM_H 106 | -------------------------------------------------------------------------------- /GLIDViewer/MyImage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define STB_IMAGE_IMPLEMENTATION 9 | #include 10 | 11 | #define STB_IMAGE_WRITE_IMPLEMENTATION 12 | #include 13 | 14 | #define STB_IMAGE_RESIZE_IMPLEMENTATION 15 | #include 16 | 17 | // int x,y,n; 18 | // unsigned char *data = stbi_load(filename, &x, &y, &n, 0); 19 | // // ... process data if not NULL ... 20 | // // ... x = width, y = height, n = # 8-bit components per pixel ... 21 | // // ... replace '0' with '1'..'4' to force that many components per pixel 22 | // // ... but 'n' will always be the number that it would have been if you said 0 23 | // stbi_image_free(data) 24 | 25 | 26 | 27 | class MyImage 28 | { 29 | private: 30 | std::vector pixels; 31 | int w, h, comp; 32 | 33 | public: 34 | MyImage():w(0),h(0),comp(0) {} 35 | ~MyImage() { } 36 | 37 | MyImage(const std::string &filename, int ncomp=4):w(0), h(0),comp(0) 38 | { 39 | stbi_set_flip_vertically_on_load(true); 40 | BYTE *data = stbi_load(filename.data(), &w, &h, &comp, ncomp); 41 | 42 | if (data) { 43 | pixels = std::vector(data, data + w*h*comp); 44 | stbi_image_free(data); 45 | } 46 | else { 47 | fprintf(stderr, "failed to load image file %s!\n", filename.c_str()); 48 | struct stat info; 49 | if ( stat(filename.c_str(), &info)) 50 | fprintf(stderr, "file doesn't exist!\n"); 51 | } 52 | } 53 | 54 | 55 | MyImage(BYTE* data, int ww, int hh, int pitch, int ncomp = 3) :w(ww), h(hh), comp(ncomp) 56 | { 57 | assert(pitch >= ww * 3); 58 | if (pitch == w*comp) pixels = std::vector(data, data + pitch*h); 59 | else { 60 | pixels.resize(w*comp*h); 61 | for (int i = 0; i < h; i++) std::copy_n(data + pitch*i, pitch, pixels.data() + i*w*comp); 62 | } 63 | } 64 | 65 | static int alignment() { return 1; } // OpenGL only supports 1,2,4,8, do not use 8, it is buggy 66 | 67 | inline bool empty() const { return pixels.empty(); } 68 | 69 | inline BYTE* data() { return pixels.data(); } 70 | inline const BYTE* data() const { return pixels.data(); } 71 | inline int width() const { return w; } 72 | inline int height() const { return h; } 73 | inline int dim() const { return comp; } 74 | inline int pitch() const { return w*comp; } 75 | 76 | MyImage rescale(int ww, int hh) 77 | { 78 | std::vector data(w*comp*h); 79 | stbir_resize_uint8(pixels.data(), w, h, 0, 80 | data.data(), ww, hh, 0, comp); 81 | 82 | return MyImage(data.data(), ww, hh, ww*comp, comp); 83 | } 84 | 85 | 86 | MyImage resizeCanvas(int ww, int hh) 87 | { 88 | std::vector data(ww*comp*hh, 255); 89 | for (int i = 0; i < h; i++) 90 | std::copy_n(pixels.data() + i*w*comp, w*comp, data.data() + i*ww*comp); 91 | 92 | return MyImage(data.data(), ww, hh, ww*comp, comp); 93 | } 94 | 95 | 96 | 97 | inline void write(const std::string &filename, bool vflip=true) const { 98 | if (filename.size() < 4 || !_strcmpi(filename.data() + filename.size() - 4, ".png")) { 99 | stbi_write_png(filename.data(), w, h, comp, pixels.data()+(vflip?w*comp*(h-1):0), w*comp*(vflip?-1:1)); 100 | } 101 | else { 102 | fprintf(stderr, "only png file format(%s) is supported for writing!\n", filename.c_str()); 103 | } 104 | } 105 | 106 | inline std::vector bits(int align=1) const 107 | { 108 | const int pitch = (w * comp + align - 1) / align*align; 109 | 110 | std::vector data(pitch*h); 111 | for(int i=0; i 4 | 5 | #ifdef _DEBUG 6 | #include 7 | #define MAKESURE(x) assert(x) 8 | #else 9 | #define MAKESURE(x) 10 | #endif 11 | 12 | template 13 | struct GLType { 14 | //static const GLenum val; 15 | //int Must_Use_Specialization[-1]; 16 | //static_assert(false, "undefined type for OpenGL"); 17 | }; 18 | 19 | template<> struct GLType { static const GLenum val = GL_BOOL; }; 20 | template<> struct GLType { static const GLenum val = GL_INT; }; 21 | template<> struct GLType { static const GLenum val = GL_FLOAT; }; 22 | template<> struct GLType { static const GLenum val = GL_DOUBLE; }; 23 | 24 | template 25 | class GLArray 26 | { 27 | public: 28 | typename R Scalar; 29 | 30 | static const GLenum GLBufferTarget = isElementArray?GL_ELEMENT_ARRAY_BUFFER:GL_ARRAY_BUFFER; 31 | static_assert( (!isElementArray) || (isElementArray && (std::is_same::value || std::is_same::value)), "the numerical type of an OpenGL element array should be (unsigned) int"); 32 | 33 | private: 34 | unsigned int handle; 35 | 36 | public: 37 | GLArray(const R *v = nullptr, int n = 0) :handle(0) { if (v) uploadData(v, n); } 38 | GLArray(GLArray&& t) { std::swap(handle, t.handle); } 39 | GLArray& operator=(GLArray&& t) { std::swap(handle, t.handle); return *this; }; 40 | GLArray(const GLArray&) = delete; 41 | GLArray& operator=(const GLArray&) = delete; 42 | 43 | ~GLArray() { if(handle) glDeleteBuffers(1, &handle); } 44 | 45 | //int dimension() const { return dim; } 46 | void bind() { MAKESURE(glIsBuffer(handle)); glBindBuffer(GLBufferTarget, handle); } 47 | void unbind() { glBindBuffer(GLBufferTarget, 0); } 48 | 49 | bool empty() const { return handle == 0; } 50 | 51 | void allocateStorage(int n) 52 | { 53 | // make sure OpenGL Context is created before this! 54 | if (handle && size() == n) return; 55 | if (!handle) glGenBuffers(1, &handle); 56 | 57 | glBindBuffer(GLBufferTarget, handle); 58 | glBufferData(GLBufferTarget, (dim * n) * sizeof(R), nullptr, GL_STATIC_DRAW); 59 | } 60 | 61 | void uploadData(const R *v, int n, int offset = 0, bool allocateIfNeeded = true) 62 | { 63 | if (!offset && allocateIfNeeded) allocateStorage(n); 64 | bind(); 65 | glBufferSubData(GLBufferTarget, (dim*offset)*sizeof(R), (dim * n) * sizeof(R), v); 66 | } 67 | 68 | void downloadData(R* v, int n, int offset = 0) 69 | { 70 | bind(); 71 | glGetBufferSubData(GLBufferTarget, (dim*offset)*sizeof(R), (dim * n) * sizeof(R), v); 72 | } 73 | 74 | void at(int i, R *x) 75 | { 76 | bind(); 77 | glGetBufferSubData(GLBufferTarget, i*dim*sizeof(R), dim* sizeof(R), x); 78 | } 79 | 80 | void setAt(int i, const R *x) 81 | { 82 | bind(); 83 | glBufferSubData(GLBufferTarget, i*dim*sizeof(R), dim* sizeof(R), x); 84 | } 85 | 86 | int size() 87 | { 88 | bind(); 89 | int n; 90 | glGetBufferParameteriv(GLBufferTarget, GL_BUFFER_SIZE, &n); 91 | return n / dim / sizeof(R); 92 | } 93 | 94 | }; 95 | 96 | -------------------------------------------------------------------------------- /GLIDViewer/glidviewer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glidviewer", "glidviewer.vcxproj", "{E8C591C6-405C-464D-9656-A165D9BB8553}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E8C591C6-405C-464D-9656-A165D9BB8553}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {E8C591C6-405C-464D-9656-A165D9BB8553}.Debug|Win32.Build.0 = Debug|Win32 18 | {E8C591C6-405C-464D-9656-A165D9BB8553}.Debug|x64.ActiveCfg = Debug|x64 19 | {E8C591C6-405C-464D-9656-A165D9BB8553}.Debug|x64.Build.0 = Debug|x64 20 | {E8C591C6-405C-464D-9656-A165D9BB8553}.Release|Win32.ActiveCfg = Release|Win32 21 | {E8C591C6-405C-464D-9656-A165D9BB8553}.Release|Win32.Build.0 = Release|Win32 22 | {E8C591C6-405C-464D-9656-A165D9BB8553}.Release|x64.ActiveCfg = Release|x64 23 | {E8C591C6-405C-464D-9656-A165D9BB8553}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /GLIDViewer/glidviewer.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {E8C591C6-405C-464D-9656-A165D9BB8553} 23 | Win32Proj 24 | glidviewer 25 | 10.0.15063.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | Application 36 | true 37 | v141 38 | Unicode 39 | 40 | 41 | Application 42 | false 43 | v141 44 | true 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | true 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | false 81 | 82 | 83 | 84 | 85 | 86 | Level4 87 | Disabled 88 | WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 89 | .;..\3rdparty\include;z:\R2015b\extern\include\;%(AdditionalIncludeDirectories) 90 | MultiThreadedDebug 91 | 92 | 93 | Console 94 | true 95 | ../3rdparty/lib/x86;z:\R2015b\extern\lib\win32\microsoft\;%(AdditionalLibraryDirectories) 96 | %(AdditionalDependencies) 97 | libcmt.lib 98 | libeng.dll;libmx.dll 99 | 100 | 101 | 102 | 103 | 104 | 105 | Level3 106 | Disabled 107 | WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 108 | .;..\3rdparty\include;z:\R2017a\extern\include\;%(AdditionalIncludeDirectories) 109 | MultiThreadedDebug 110 | 111 | 112 | Console 113 | true 114 | ../3rdparty/lib/x64;z:\R2017a\extern\lib\win64\microsoft\;%(AdditionalLibraryDirectories) 115 | libeng.dll;libmx.dll 116 | 117 | 118 | %(AdditionalDependencies) 119 | 120 | 121 | echo copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir).. 122 | copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir).. 123 | 124 | 125 | 126 | 127 | Level4 128 | 129 | 130 | MaxSpeed 131 | true 132 | true 133 | WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 134 | .;..\3rdparty\include;z:\R2015b\extern\include\;%(AdditionalIncludeDirectories) 135 | MultiThreaded 136 | 137 | 138 | Console 139 | true 140 | true 141 | true 142 | ../3rdparty/lib/x86;z:\R2015b\extern\lib\win32\microsoft\;%(AdditionalLibraryDirectories) 143 | %(AdditionalDependencies) 144 | libeng.dll;libmx.dll 145 | 146 | 147 | 148 | 149 | Level3 150 | 151 | 152 | MaxSpeed 153 | true 154 | true 155 | WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 156 | .;..\3rdparty\include;z:\R2017a\extern\include\;%(AdditionalIncludeDirectories) 157 | MultiThreaded 158 | /Zo %(AdditionalOptions) 159 | 160 | 161 | Console 162 | true 163 | true 164 | true 165 | ../3rdparty/lib/x64;z:\R2017a\extern\lib\win64\microsoft\;%(AdditionalLibraryDirectories) 166 | libeng.dll;libmx.dll 167 | %(AdditionalDependencies) 168 | 169 | 170 | echo copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir).. 171 | copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir).. 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /GLIDViewer/glidviewer.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /GLIDViewer/glidviewer.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /GLIDViewer/matlabDeformer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "deformer.h" 4 | #include "matlab_utils.h" 5 | #include 6 | #include 7 | 8 | extern bool showATB; 9 | extern int viewport[4]; 10 | void display(); 11 | void loadP2PConstraints(); 12 | 13 | inline std::string catStr(const std::vector &names) 14 | { 15 | std::string str; 16 | for (int i = 0; i < names.size(); i++) { 17 | str += names[i]; 18 | if (i < names.size() - 1) str += ", "; 19 | } 20 | return str; 21 | } 22 | 23 | struct MatlabDeformer : public Deformer 24 | { 25 | std::vector solver_names; //const char *[] = { "newton", ... }; 26 | std::vector energy_names; //const char *[] = { 'ISO', 'EISO', 'AMIPS' }; 27 | int solver = 0; 28 | int energy_type = 0; 29 | 30 | MyMesh &M; 31 | 32 | MatlabDeformer(MatlabDeformer&) = delete; 33 | 34 | MatlabDeformer(MyMesh &m) :M(m){ 35 | 36 | using deformerptr = MatlabDeformer*; 37 | 38 | TwBar *bar = TwNewBar("GLIDDeformer"); 39 | 40 | TwDefine(" GLIDDeformer size='220 180' color='255 0 255' text=dark alpha=128 position='5 300' label='GLID Deformer'"); // change default tweak bar size and color 41 | 42 | ////////////////////////////////////////////////////////////////////////// 43 | solver_names = matlab2strings("harmonic_map_solvers"); 44 | std::string defaultsolver = matlab2string("default_harmonic_map_solver"); 45 | for (int i = 0; i < solver_names.size(); i++) if (defaultsolver == solver_names[i]) solver = i; 46 | 47 | energy_names = matlab2strings("harmonic_map_energies"); 48 | std::string defaultenergy = matlab2string("default_harmonic_map_energy"); 49 | for (int i = 0; i < energy_names.size(); i++) if (defaultenergy == energy_names[i]) energy_type = i; 50 | 51 | TwType energyType = TwDefineEnumFromString("Energy", catStr(energy_names).c_str()); 52 | TwAddVarRW(bar, "Energy", energyType, &energy_type, " "); 53 | 54 | TwType solverType = TwDefineEnumFromString("Solver", catStr(solver_names).c_str()); 55 | TwAddVarRW(bar, "Solver", solverType, &solver, " "); 56 | 57 | 58 | ////////////////////////////////////////////////////////////////////////// 59 | TwAddVarCB(bar, "P2P weight", TW_TYPE_FLOAT, 60 | [](const void *v, void *) { scalar2matlab("p2p_weight", *(const float*)(v)); }, 61 | [](void *v, void *) { *(float*)(v) = matlab2scalar("p2p_weight"); }, 62 | nullptr, " min=0 "); 63 | 64 | TwAddVarCB(bar, "#Samples", TW_TYPE_INT32, 65 | [](const void *v, void *d) { scalar2matlab("numEnergySamples", *(const int*)(v)); 66 | matlabEval("p2p_harmonic_prep;"); 67 | }, 68 | [](void *v, void *) { *(int*)(v) = matlab2scalar("numEnergySamples"); }, 69 | nullptr, " min=100 "); 70 | 71 | TwAddVarCB(bar, "energy param", TW_TYPE_FLOAT, 72 | [](const void *v, void *d) { scalar2matlab("energy_parameter", *(const float*)(v)); }, 73 | [](void *v, void *) { *(float*)(v) = matlab2scalar("energy_parameter"); }, 74 | nullptr, " min=0 help='change the parameter for some energies, e.g. AMIPS, BARAP, power iso' "); 75 | 76 | TwAddButton(bar, "Reset View", [](void *d) { 77 | deformerptr(d)->M.updateBBox(); 78 | deformerptr(d)->M.mMeshScale = 1.f; 79 | deformerptr(d)->M.mTranslate.assign(0.f); 80 | }, this, " "); 81 | 82 | TwAddButton(bar, "Reset Shape", [](void *d) { 83 | deformerptr(d)->resetDeform(); 84 | matlabEval("NLO_preprocessed = false; P2P_Deformation_Converged = 0;"); }, this, " key=r "); 85 | 86 | TwAddVarCB(bar, "Pause", TW_TYPE_BOOLCPP, 87 | [](const void *v, void *d) { deformerptr(d)->needIteration = !*(bool*)(v); }, 88 | [](void *v, void *d) { *(bool*)(v) = !deformerptr(d)->needIteration; }, 89 | this, " key=i "); 90 | 91 | TwAddButton(bar, "Clear P2P", [](void *d) { 92 | deformerptr(d)->M.constrainVertices.clear(); 93 | deformerptr(d)->M.actConstrainVertex = -1; 94 | deformerptr(d)->updateP2PConstraints(-1); }, 95 | this, " "); 96 | 97 | preprocess(); 98 | } 99 | 100 | ~MatlabDeformer(){ 101 | TwBar *bar = TwGetBarByName("GLIDDeformer"); 102 | if (bar) TwDeleteBar(bar); 103 | } 104 | 105 | virtual std::string name(){ return "P2PHarmonic"; } 106 | 107 | virtual void preprocess() 108 | { 109 | matlabEval("p2p_harmonic_prep;"); 110 | 111 | deformResultFromMaltab("XP2PDeform"); 112 | } 113 | 114 | 115 | virtual void updateP2PConstraints(int) 116 | { 117 | using namespace Eigen; 118 | const size_t nConstrain = M.constrainVertices.size(); 119 | eigen2matlab("P2PVtxIds", (Map(M.getConstrainVertexIds().data(), nConstrain) + VectorXi::Ones(nConstrain)).cast()); 120 | matlabEval("CauchyCoordinatesAtP2Phandles = C(P2PVtxIds,:);"); 121 | 122 | MatrixX2d p2pdst = Map >(M.getConstrainVertexCoords().data(), nConstrain, 2).cast(); 123 | eigen2matlabComplex("P2PCurrentPositions", p2pdst.col(0), p2pdst.col(1)); 124 | } 125 | 126 | 127 | void deformResultFromMaltab(std::string resVarName) 128 | { 129 | using namespace Eigen; 130 | MatrixXcd x = matlab2eigenComplex(resVarName); 131 | if (x.rows() == 0) return; 132 | 133 | Matrix xr(x.rows(), 2); 134 | xr.col(0) = x.real().cast(); 135 | xr.col(1) = x.imag().cast(); 136 | M.upload(xr, Eigen::MatrixXi(), nullptr); 137 | } 138 | 139 | virtual void deform() 140 | { 141 | string2matlab("solver_type", solver_names[solver]); 142 | string2matlab("energy_type", energy_names[energy_type]); 143 | matlabEval("p2p_harmonic;"); 144 | 145 | deformResultFromMaltab("XP2PDeform"); 146 | } 147 | 148 | virtual bool converged() { 149 | return !getMatEngine().hasVar("P2P_Deformation_Converged") || matlab2scalar("P2P_Deformation_Converged") > 0; 150 | } 151 | 152 | virtual void resetDeform() { 153 | matlabEval("Phi = vv; Psy = Phi * 0; XP2PDeform = X; phipsyIters = [];"); 154 | deformResultFromMaltab("XP2PDeform"); 155 | } 156 | virtual void getResult() {} 157 | virtual void saveData() { matlabEval("p2p_harmonic_savedata;"); } 158 | }; 159 | -------------------------------------------------------------------------------- /GLIDViewer/matlab_utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace Eigen; 3 | 4 | #include "matlab_utils.h" 5 | 6 | #ifdef ENABLE_MATLAB 7 | 8 | MatlabEngine gMatEngine; 9 | 10 | // Matlab engine 11 | #pragma comment(lib, "libeng.lib") 12 | #pragma comment(lib, "libmx.lib") 13 | 14 | bool MatlabEngine::connect(const std::string &dir, bool closeAll) 15 | { 16 | if (eng) 17 | return true; 18 | 19 | if (0) { 20 | // 0 = success 21 | // -2 = error - second argument must be NULL 22 | // -3 = error - engOpenSingleUse failed 23 | int retstatus; 24 | if ( !(eng = engOpenSingleUse("\0", NULL, &retstatus)) ) { 25 | fprintf(stderr, "Can't start MATLAB engine: %d\n", retstatus); 26 | return false; 27 | } 28 | } 29 | else { 30 | printf( "Starting MATLAB engine ... " ); 31 | if (!(eng = engOpen("\0"))) { 32 | fprintf(stderr, "Failed!\n"); 33 | return false; 34 | } 35 | printf( "Succeed!\n"); 36 | } 37 | 38 | engBuffer[lenEngBuffer-1] = '\0'; 39 | engOutputBuffer(eng, engBuffer, lenEngBuffer); 40 | 41 | // set current path 42 | engEvalString(eng, ("cd " + dir).c_str()); 43 | 44 | //engEvalString(eng, "clear all; clc; rehash;"); 45 | if (closeAll) 46 | engEvalString(eng, "close all;"); 47 | 48 | return true; 49 | } 50 | 51 | void MatlabEngine::eval(const std::string &cmd) 52 | { 53 | ensure(connected(), "Not connected to Matlab!"); 54 | 55 | if (consoleOutput) 56 | engOutputBuffer(eng, engBuffer, lenEngBuffer); 57 | else 58 | engOutputBuffer(eng, nullptr, 0); 59 | 60 | //engEvalString(eng, "dbclear all;"); 61 | engEvalString(eng, "if ~isempty(dbstatus), warning('Clear all breakpoint, Matlab cannot be debugged from C++!'); dbclear all; end"); // debug will cause matlab to crash 62 | if (consoleOutput && *engBuffer) fprintf(stderr, "%s\n", engBuffer); 63 | 64 | int r = engEvalString(eng, cmd.c_str()); 65 | if (r != 0) { 66 | if (r == 1) fprintf(stderr, "Engine session no longer running!\n"); 67 | 68 | fprintf(stderr, "engEvalString error!\n"); 69 | } 70 | 71 | if (consoleOutput && *engBuffer) { 72 | fprintf(stdout, "--------------\n%s\n--------------\n%s\n", cmd.c_str(), engBuffer); 73 | } 74 | } 75 | 76 | 77 | void MatlabEngine::close() 78 | { 79 | printf( "Shutting down MATLAB engine ... " ); 80 | engClose(eng); 81 | printf( "done!\n" ); 82 | eng = nullptr; 83 | } 84 | 85 | MatlabEngine& getMatEngine() 86 | { 87 | return gMatEngine; 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /GLID_main.m: -------------------------------------------------------------------------------- 1 | enableservice('AutomationServer', true); 2 | !start glidviewer.exe 3 | 4 | -------------------------------------------------------------------------------- /GetCompCage.m: -------------------------------------------------------------------------------- 1 | function cage = GetCompCage(Img, offset, simplify, flag1, showResult) 2 | 3 | % flag1: normalize to [-1 1] 4 | 5 | [im,~,mask]=imread(Img); 6 | 7 | s = 1; 8 | if size(mask,1)>3000 9 | s = 3000/size(mask,1); 10 | mask = imresize(mask, s); 11 | end 12 | 13 | xs = cellfun(@(x) x/s, AutoCage(mask,1,offset,simplify,flag1), 'UniformOutput', false); 14 | [w, h, ~] = size(im); 15 | 16 | xs = cellfun(@(x) x(1:end-1,:), xs, 'UniformOutput', false); 17 | cage = cellfun(@(x) x(end:-1:1, :)*[-1i; 1]/100 - (h-w*1i)/200, xs, 'UniformOutput', false); 18 | 19 | if showResult 20 | figure; image(im); hold on; axis equal; 21 | cellfun(@(x) plot( conj(x([1:end 1])*100+h/2-w/2*1i), 'r', 'linewidth', 2 ), cage) 22 | end 23 | 24 | end 25 | 26 | function B=AutoCage(Mask,scale,offset,simplify,flag1) 27 | % Mask: binary image 28 | % scale: set to 1 29 | % offset: number of dilations to do 30 | % simplify: maximal distance of the simplified polygon from the dilated 31 | % boundary of the domain 32 | MaskSc=double(imresize(Mask,scale)); 33 | B = bwboundaries(bwmorph(MaskSc,'dilate',offset),'noholes'); 34 | SimpB = cellfun(@(b) dpsimplify(b,simplify), B, 'UniformOutput', false); 35 | SimpB = SimpB( cellfun(@(b) size(b,1)>2, SimpB) ); 36 | 37 | if flag1==1 38 | B = cellfun(@(b) 2*(b-1)./(size(MaskSc)-1)-1, 'UniformOutput', false); 39 | else 40 | B = SimpB; 41 | end 42 | end 43 | 44 | function [ps,ix] = dpsimplify(p,tol) 45 | 46 | % Recursive Douglas-Peucker Polyline Simplification, Simplify 47 | % 48 | % [ps,ix] = dpsimplify(p,tol) 49 | % 50 | % dpsimplify uses the recursive Douglas-Peucker line simplification 51 | % algorithm to reduce the number of vertices in a piecewise linear curve 52 | % according to a specified tolerance. The algorithm is also know as 53 | % Iterative Endpoint Fit. It works also for polylines and polygons 54 | % in higher dimensions. 55 | % 56 | % In case of nans (missing vertex coordinates) dpsimplify assumes that 57 | % nans separate polylines. As such, dpsimplify treats each line 58 | % separately. 59 | % 60 | % For additional information on the algorithm follow this link 61 | % http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm 62 | % 63 | % Input arguments 64 | % 65 | % p polyline n*d matrix with n vertices in d 66 | % dimensions. 67 | % tol tolerance (maximal euclidean distance allowed 68 | % between the new line and a vertex) 69 | % 70 | % Output arguments 71 | % 72 | % ps simplified line 73 | % ix linear index of the vertices retained in p (ps = p(ix)) 74 | % 75 | % Examples 76 | % 77 | % 1. Simplify line 78 | % 79 | % tol = 1; 80 | % x = 1:0.1:8*pi; 81 | % y = sin(x) + randn(size(x))*0.1; 82 | % p = [x' y']; 83 | % ps = dpsimplify(p,tol); 84 | % 85 | % plot(p(:,1),p(:,2),'k') 86 | % hold on 87 | % plot(ps(:,1),ps(:,2),'r','LineWidth',2); 88 | % legend('original polyline','simplified') 89 | % 90 | % 2. Reduce polyline so that only knickpoints remain by 91 | % choosing a very low tolerance 92 | % 93 | % p = [(1:10)' [1 2 3 2 4 6 7 8 5 2]']; 94 | % p2 = dpsimplify(p,eps); 95 | % plot(p(:,1),p(:,2),'k+--') 96 | % hold on 97 | % plot(p2(:,1),p2(:,2),'ro','MarkerSize',10); 98 | % legend('original line','knickpoints') 99 | % 100 | % 3. Simplify a 3d-curve 101 | % 102 | % x = sin(1:0.01:20)'; 103 | % y = cos(1:0.01:20)'; 104 | % z = x.*y.*(1:0.01:20)'; 105 | % ps = dpsimplify([x y z],0.1); 106 | % plot3(x,y,z); 107 | % hold on 108 | % plot3(ps(:,1),ps(:,2),ps(:,3),'k*-'); 109 | % 110 | % 111 | % 112 | % Author: Wolfgang Schwanghart, 13. July, 2010. 113 | % w.schwanghart[at]unibas.ch 114 | 115 | 116 | if nargin == 0 117 | help dpsimplify 118 | return 119 | end 120 | 121 | narginchk(2, 2) 122 | 123 | % error checking 124 | if ~isscalar(tol) || tol<0; 125 | error('tol must be a positive scalar') 126 | end 127 | 128 | 129 | % nr of dimensions 130 | nrvertices = size(p,1); 131 | dims = size(p,2); 132 | 133 | % anonymous function for starting point and end point comparision 134 | % using a relative tolerance test 135 | compare = @(a,b) abs(a-b)/max(abs(a),abs(b)) <= eps; 136 | 137 | % what happens, when there are NaNs? 138 | % NaNs divide polylines. 139 | Inan = any(isnan(p),2); 140 | % any NaN at all? 141 | Inanp = any(Inan); 142 | 143 | % if there is only one vertex 144 | if nrvertices == 1 || isempty(p); 145 | ps = p; 146 | ix = 1; 147 | 148 | % if there are two 149 | elseif nrvertices == 2 && ~Inanp; 150 | % when the line has no vertices (except end and start point of the 151 | % line) check if the distance between both is less than the tolerance. 152 | % If so, return the center. 153 | if dims == 2; 154 | d = hypot(p(1,1)-p(2,1),p(1,2)-p(2,2)); 155 | else 156 | d = sqrt(sum((p(1,:)-p(2,:)).^2)); 157 | end 158 | 159 | if d <= tol; 160 | ps = sum(p,1)/2; 161 | ix = 1; 162 | else 163 | ps = p; 164 | ix = [1;2]; 165 | end 166 | 167 | elseif Inanp; 168 | 169 | % case: there are nans in the p array 170 | % --> find start and end indices of contiguous non-nan data 171 | Inan = ~Inan; 172 | sIX = strfind(Inan',[0 1])' + 1; 173 | eIX = strfind(Inan',[1 0])'; 174 | 175 | if Inan(end)==true; 176 | eIX = [eIX;nrvertices]; 177 | end 178 | 179 | if Inan(1); 180 | sIX = [1;sIX]; 181 | end 182 | 183 | % calculate length of non-nan components 184 | lIX = eIX-sIX+1; 185 | % put each component into a single cell 186 | c = mat2cell(p(Inan,:),lIX,dims); 187 | 188 | % now call dpsimplify again inside cellfun. 189 | if nargout == 2; 190 | [ps,ix] = cellfun(@(x) dpsimplify(x,tol),c,'uniformoutput',false); 191 | ix = cellfun(@(x,six) x+six-1,ix,num2cell(sIX),'uniformoutput',false); 192 | else 193 | ps = cellfun(@(x) dpsimplify(x,tol),c,'uniformoutput',false); 194 | end 195 | 196 | % write the data from a cell array back to a matrix 197 | ps = cellfun(@(x) [x;nan(1,dims)],ps,'uniformoutput',false); 198 | ps = cell2mat(ps); 199 | ps(end,:) = []; 200 | 201 | % ix wanted? write ix to a matrix, too. 202 | if nargout == 2; 203 | ix = cell2mat(ix); 204 | end 205 | 206 | 207 | else 208 | 209 | 210 | % if there are no nans than start the recursive algorithm 211 | ixe = size(p,1); 212 | ixs = 1; 213 | 214 | % logical vector for the vertices to be retained 215 | I = true(ixe,1); 216 | 217 | % call recursive function 218 | p = simplifyrec(p,tol,ixs,ixe); 219 | ps = p(I,:); 220 | 221 | % if desired return the index of retained vertices 222 | if nargout == 2; 223 | ix = find(I); 224 | end 225 | 226 | end 227 | 228 | % _________________________________________________________ 229 | function p = simplifyrec(p,tol,ixs,ixe) 230 | 231 | % check if startpoint and endpoint are the same 232 | % better comparison needed which included a tolerance eps 233 | 234 | c1 = num2cell(p(ixs,:)); 235 | c2 = num2cell(p(ixe,:)); 236 | 237 | % same start and endpoint with tolerance 238 | sameSE = all(cell2mat(cellfun(compare,c1(:),c2(:),'UniformOutput',false))); 239 | 240 | 241 | if sameSE; 242 | % calculate the shortest distance of all vertices between ixs and 243 | % ixe to ixs only 244 | if dims == 2; 245 | d = hypot(p(ixs,1)-p(ixs+1:ixe-1,1),p(ixs,2)-p(ixs+1:ixe-1,2)); 246 | else 247 | d = sqrt(sum(bsxfun(@minus,p(ixs,:),p(ixs+1:ixe-1,:)).^2,2)); 248 | end 249 | else 250 | % calculate shortest distance of all points to the line from ixs to ixe 251 | % subtract starting point from other locations 252 | pt = bsxfun(@minus,p(ixs+1:ixe,:),p(ixs,:)); 253 | 254 | % end point 255 | a = pt(end,:)'; 256 | 257 | beta = (a' * pt')./(a'*a); 258 | b = pt-bsxfun(@times,beta,a)'; 259 | if dims == 2; 260 | % if line in 2D use the numerical more robust hypot function 261 | d = hypot(b(:,1),b(:,2)); 262 | else 263 | d = sqrt(sum(b.^2,2)); 264 | end 265 | end 266 | 267 | % identify maximum distance and get the linear index of its location 268 | [dmax,ixc] = max(d); 269 | ixc = ixs + ixc; 270 | 271 | % if the maximum distance is smaller than the tolerance remove vertices 272 | % between ixs and ixe 273 | if dmax <= tol; 274 | if ixs ~= ixe-1; 275 | I(ixs+1:ixe-1) = false; 276 | end 277 | % if not, call simplifyrec for the segments between ixs and ixc (ixc 278 | % and ixe) 279 | else 280 | p = simplifyrec(p,tol,ixs,ixc); 281 | p = simplifyrec(p,tol,ixc,ixe); 282 | 283 | end 284 | 285 | end 286 | end 287 | 288 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This package contains the code that implements the following paper, 2 | 3 | Renjie Chen and Ofir Weber. 4 | GPU-Accelerated Locally Injective Shape Deformation. 5 | ACM Transactions on Graphics, 36(6) (SIGGRAPH Asia 2017) 6 | 7 | # What does the code contain. 8 | The app is built with a combination of MATLAB (for core computation), C++ code (for the UI) and mex/CUDA code (for GPU accelerated optimization). 9 | The C++ source code for the OpenGL UI with MS Visual Studio C++ project files is in the glidviewer folder. 10 | The mex/CUDA source code for the GPU accelerated optimization is in the cuHarmonic folder. 11 | Precompiled binary for the UI and mex/CUDA are provided with the package. 12 | 13 | # Requirements: 14 | - MS Windows (Windows 7/8/10) 15 | - MATLAB (>2016b) 16 | - A GLSL 3.3 compatible GPU. 17 | - The OpenGL UI (glidviewer.exe) 18 | - CUDA (Compute Capability > 3.5) 19 | 20 | # To run the software: 21 | 1. Start MATLAB 22 | 2. cd to the code folder 23 | 3. Call glid_main.m within MATLAB. This will automatically open the main GUI, and load the rex shape 24 | 25 | ### The User Interface (main options): 26 | 4. For deformation, the p2p constraint can be edited by 27 | * adding P2P constraints by left clicking on the shape 28 | * moving the p2p target by dragging any p2p constraint 29 | * removing constraints by right clicking the p2p 30 | 5. GLID Deformer widget 31 | * Energy, isometric energy for the optimization 32 | * Solver, including mesh based AQP and SLIM, and harmonic subspace based Gradient Descent, LBFGS, Newton etc. 33 | * #samples, number of samples on the boundary for the boundary integral approximation 34 | * energy param, the paramter s for Exp Symmetric Dirichlet and AMIPS energies 35 | * Reset Shape, reset the shape to its original state (identity mapping) 36 | * Pause, paurse the iteration 37 | * Clear P2P: remove all the p2p constraints. 38 | 39 | # How to compile the binaries 40 | The following libraries are needed to compile the code 41 | 1. OpenGL GUI (glidviewer.exe) 42 | * Eigen 43 | http://eigen.tuxfamily.org 44 | * AntTweakBar 45 | http://anttweakbar.sourceforge.net 46 | * FreeGLUT 47 | http://freeglut.sourceforge.net 48 | 49 | 2. GPU-accelerated solver (cuHarmonic.mexw64) 50 | * cub library 51 | https://nvlabs.github.io/cub/ 52 | 53 | -------------------------------------------------------------------------------- /cauchyCoordinates.m: -------------------------------------------------------------------------------- 1 | function C = cauchyCoordinates(cage, z, holeCenters) 2 | % Compute regular Cauchy coordiantes for given cage at strictly inside points z (z are not allowed to be on the boundary) 3 | % 4 | % Input parameters: 5 | % cage - cage 6 | % z - points inside the cage 7 | 8 | z = reshape(z, [], 1); 9 | remove2coeffAtHoles = true; 10 | 11 | if iscell(cage) 12 | C = cellfun(@(c) cauchyCoordinates(c,z), cage, 'UniformOutput', false); 13 | C = cat(2, C{:}); 14 | 15 | % remove 2 holomorphic DOF for each hole 16 | if remove2coeffAtHoles 17 | cageSizes = cellfun(@numel, cage); 18 | flags = true(sum(cageSizes), 1); 19 | flags( reshape(cumsum(cageSizes(1:end-1)),[],1) + [1 2] ) = false; 20 | C = C(:, flags); 21 | end 22 | 23 | if numel(cage)>1 24 | assert(nargin>2 && ~isempty(holeCenters)); 25 | 26 | holeCenters = reshape(holeCenters, 1, []); 27 | C = [C log( abs(z-holeCenters) )]; % a*2*log|z-z0| = a*log|z-z0| + conj( conj(a*log|z-z0|) ) = a*log(z-z0) + conj( conj(a*log(z-z0)) ) 28 | end 29 | else 30 | 31 | Aj = cage - cage([end 1:end-1]); 32 | Ajp = Aj([2:end 1], :); 33 | 34 | Bj = bsxfun(@minus, cage.', z); 35 | Bjp = Bj(:, [2:end 1]); 36 | Bjm = Bj(:, [end 1:end-1]); 37 | 38 | 39 | oneOver2pi_i = 1/(2*pi*1i); 40 | 41 | %C = oneOver2pi_i*((Bjp./Ajp).*log(Bjp./Bj) - (Bjm./Aj).*log(Bj./Bjm)); 42 | 43 | C = oneOver2pi_i*(Bjp.*bsxfun(@rdivide, log(Bjp./Bj), Ajp.') - Bjm.*bsxfun(@rdivide, log(Bj./Bjm), Aj.')); 44 | 45 | end -------------------------------------------------------------------------------- /cdt.m: -------------------------------------------------------------------------------- 1 | function [X,T]=cdt(xb, xi, nTri, keepBoundary, arg) 2 | 3 | if nargin<2 || isempty(xi), xi = zeros(0, 2); end 4 | if nargin<4, keepBoundary = false; end 5 | 6 | if ~iscell(xb) && ~isreal(xb), xb = [real(xb) imag(xb)]; end 7 | 8 | if ~iscell(xb), assert( size(xb, 2)==2, 'b is not a matrix with 2 columns, transposed?' ); end; 9 | 10 | triangle = which('triangle.bin'); 11 | assert(~isempty(triangle), 'Can''t find triangle application'); 12 | 13 | filename = tempname; 14 | writePoly([filename '.poly'], xb, xi); 15 | 16 | 17 | if nargin<5 18 | if nargin<3 || nTri<=0 19 | averArea = 0; 20 | else 21 | if ~iscell(xb) 22 | averArea = polyarea(xb(:,1)', xb(:,2)', 2)/nTri; 23 | else 24 | averArea = abs( sum(cellfun(@signedpolyarea, xb))/nTri ); 25 | end 26 | end 27 | 28 | %% 29 | % -Y Prohibits the insertion of Steiner points on the mesh boundary 30 | % -c including convex hull into the triangulation, 31 | % -q50 for specifying min angle 32 | arg = ' -g '; 33 | if keepBoundary, arg = [arg '-Y ']; end 34 | % use num2str(..., '%f') to fix a bug of not getting target # triangles 35 | if averArea>0, arg = [arg '-a' num2str(averArea, '%.15f') ' ']; end 36 | evalc(['!"' triangle '" -q30' arg filename '.poly']); 37 | % evalc(['!"' triangle '" ' arg filename '.poly']); 38 | % eval(['!"' triangle '" -c -q50 -g -Y ' filename '.poly']); 39 | else % use input arg 40 | evalc(['!"' triangle '" -g' arg ' ' filename '.poly']); 41 | end 42 | 43 | %% 44 | [X, T] = readOff( [filename '.1.off'] ); 45 | X = X(:, 1:2); 46 | 47 | eval( ['delete ' filename '.poly'] ); 48 | eval( ['delete ' filename '.1.node'] ); 49 | eval( ['delete ' filename '.1.ele'] ); 50 | eval( ['delete ' filename '.1.poly'] ); 51 | eval( ['delete ' filename '.1.off'] ); 52 | 53 | 54 | %% important for some mesh processing. Not clear why Triangle produce these isolated vertices 55 | if ~all( sparse(1, T, 1, 1, size(X,1)) ) 56 | nv0 = size(X, 1); 57 | [X, T] = removeIsolatedVerticesInMesh(X, T); 58 | warning('%d isolated vertices produced by Triangle have been removed', nv0-size(X,1)); 59 | end 60 | 61 | 62 | 63 | function [X, T] = readOff(filename) 64 | 65 | %% 66 | [fid, errmsg] = fopen(filename, 'r'); 67 | if fid == -1, error(errmsg); end 68 | cleanupObj = onCleanup(@()fclose(fid)); 69 | 70 | %% 71 | str = textscan(fid, '%s', 1, 'CommentStyle', '#'); 72 | if ~strcmp(str{1}, 'OFF'), error('wrong off file signature'); end 73 | 74 | str = textscan(fid, '%d %d %d', 1, 'CommentStyle', '#'); 75 | nv = str{1}; 76 | nf = str{2}; 77 | 78 | %% 79 | X = cell2mat( textscan(fid, '%f %f %f', nv, 'CommentStyle', '#') ); 80 | 81 | %% 82 | pos = ftell(fid); 83 | tsz = textscan(fid, '%f %*[^\n]', nf, 'CommentStyle', '#'); 84 | fseek(fid, pos, 'bof'); 85 | 86 | %% 87 | if all(tsz{1}==3) 88 | T = cell2mat( textscan(fid, '%*f %f %f %f', nf, 'CommentStyle', '#') ) + 1; 89 | else 90 | T = cell(nf, 1); 91 | for i=1:nf 92 | str = textscan(fid, '%f', 1, 'CommentStyle', '#'); 93 | str = textscan(fid, '%f', str{1}, 'CommentStyle', '#'); 94 | 95 | T{i} = reshape(str{1}, 1, []) + 1; 96 | end 97 | end 98 | 99 | 100 | function writePoly(filename, xb, xi, edges) 101 | 102 | if nargin<3, xi = zeros(0, 2); end 103 | if nargin<4, edges = zeros(0, 2); end 104 | 105 | [fid, errmsg] = fopen(filename, 'wt'); 106 | if fid == -1, error(errmsg); end 107 | cleanupObj = onCleanup(@()fclose(fid)); 108 | 109 | if iscell(xb) 110 | if ~isreal( xb{1} ) 111 | xb = cellfun(@(x) [real(x) imag(x)], xb, 'UniformOutput',false); 112 | end 113 | 114 | holes = xb(2:end); 115 | xb = xb{1}; 116 | else 117 | holes = {}; 118 | end 119 | 120 | nH = numel(holes); 121 | nb = size(xb,1); 122 | nhv = sum( cellfun(@length, holes) ); 123 | nv = size(xi,1) + nb + nhv; 124 | 125 | %% vertices 126 | fprintf(fid, '%d 2 0 0\n', nv); 127 | fprintf(fid, '%d %.8f %.8f\n', [1:nv; xb(:,1:2)' xi(:,1:2)' cat(1,holes{:})'] ); 128 | 129 | %% edges 130 | nie = size(edges,1); 131 | fprintf(fid, '%d 0\n', nb+nie+nhv); 132 | 133 | fGenPolyEdges = @(n) [1:n; 2:n 1]; 134 | 135 | e = [fGenPolyEdges(nb) edges'+nb]; 136 | for i=1:nH 137 | n = nv - sum( cellfun(@length, holes(i:end)) ); 138 | e = [e fGenPolyEdges(size(holes{i}, 1 ))+n]; 139 | end 140 | 141 | fprintf(fid, '%d %d %d\n', [1:size(e,2); e] ); 142 | % fprintf(fid, '%d %d %d\n', [1:nb; 1:nb; [2:nb 1]] ); 143 | % fprintf(fid, '%d %d %d\n', [(1:nie)+nb; edges'] ); 144 | 145 | %% holes 146 | fC2R = @(x) [real(x); imag(x)]; 147 | fprintf(fid, '%d\n', nH); 148 | fprintf(fid, '%d %.8f %.8f\n', [1:nH; fC2R( cellfun( @findPointInPolygon, holes ) )] ); 149 | 150 | 151 | function c = findPointInPolygon(b) 152 | 153 | fR2C = @(x) complex(x(:,1), x(:,2)); 154 | if isreal(b), b=fR2C(b); end 155 | 156 | dt = delaunayTriangulation(real(b), imag(b), [1:numel(b); 2:numel(b) 1]'); 157 | 158 | cc = fR2C(dt.circumcenter); 159 | d = abs( b(dt(:,1)) - cc ); 160 | d( ~inpolygon(real(cc), imag(cc), real(b), imag(b)) ) = 0; 161 | 162 | [~, i] = max(d); 163 | c = cc(i); 164 | -------------------------------------------------------------------------------- /cuHarmonic.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/cuHarmonic.mexw64 -------------------------------------------------------------------------------- /cuHarmonic/cuHarmonic.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.15 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cuHarmonic", "cuHarmonic.vcxproj", "{C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5}.Debug|x64.ActiveCfg = Debug|x64 17 | {C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5}.Debug|x64.Build.0 = Debug|x64 18 | {C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5}.Debug|x86.ActiveCfg = Debug|Win32 19 | {C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5}.Debug|x86.Build.0 = Debug|Win32 20 | {C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5}.Release|x64.ActiveCfg = Release|x64 21 | {C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5}.Release|x64.Build.0 = Release|x64 22 | {C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5}.Release|x86.ActiveCfg = Release|Win32 23 | {C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /cuHarmonic/cuHarmonic.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {C3C7806D-9EA4-40B2-9ECA-D1E43BF78FA5} 23 | Win32Proj 24 | cuHarmonic 25 | 10.0.15063.0 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v140 32 | Unicode 33 | 34 | 35 | DynamicLibrary 36 | false 37 | v140 38 | true 39 | Unicode 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v140 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v140 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | .mexw64 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | .mexw64 86 | 87 | 88 | 89 | 90 | 91 | Level3 92 | Disabled 93 | WIN32;_DEBUG;_WINDOWS;_USRDLL;CU_HARMONIC_EXPORTS;%(PreprocessorDefinitions) 94 | 95 | 96 | Windows 97 | true 98 | 99 | 100 | 101 | 102 | 103 | 104 | Level3 105 | Disabled 106 | _DEBUG;_WINDOWS;_USRDLL;CU_HARMONIC_EXPORTS;%(PreprocessorDefinitions) 107 | .;z:\R2017a\extern\include;z:\R2017a\toolbox\distcomp\gpu\extern\include;%(AdditionalIncludeDirectories);$(CudaToolkitIncludeDir) 108 | 109 | 110 | Windows 111 | true 112 | z:\R2017a\extern\lib\win64\microsoft\;%(AdditionalLibraryDirectories) 113 | cudadevrt.lib;cusolver.lib;cudart_static.lib;cublas.lib;libmx.lib;gpu.lib;libmex.lib;%(AdditionalDependencies) 114 | mex.def 115 | 116 | 117 | 118 | 119 | --expt-extended-lambda %(AdditionalOptions) 120 | true 121 | compute_52,sm_52 122 | InheritFromHost 123 | _DEBUG 124 | 125 | 126 | echo copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir).. 127 | copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir).. 128 | 129 | 130 | 131 | 132 | Level3 133 | 134 | 135 | MaxSpeed 136 | true 137 | true 138 | WIN32;NDEBUG;_WINDOWS;_USRDLL;CU_HARMONIC_EXPORTS;%(PreprocessorDefinitions) 139 | 140 | 141 | Windows 142 | true 143 | true 144 | true 145 | 146 | 147 | 148 | 149 | Level3 150 | 151 | 152 | MaxSpeed 153 | true 154 | true 155 | NDEBUG;_WINDOWS;_USRDLL;CU_HARMONIC_EXPORTS;%(PreprocessorDefinitions) 156 | .;z:\R2017a\extern\include;z:\R2017a\toolbox\distcomp\gpu\extern\include;%(AdditionalIncludeDirectories);$(CudaToolkitIncludeDir) 157 | 158 | 159 | Windows 160 | true 161 | true 162 | true 163 | z:\R2017a\extern\lib\win64\microsoft\;%(AdditionalLibraryDirectories) 164 | cudadevrt.lib;cusolver.lib;cudart_static.lib;cublas.lib;libmx.lib;gpu.lib;libmex.lib;%(AdditionalDependencies) 165 | mex.def 166 | 167 | 168 | 169 | 170 | --expt-extended-lambda %(AdditionalOptions) 171 | compute_35,sm_35 172 | true 173 | InheritFromHost 174 | 175 | 176 | echo copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir).. 177 | copy $(OutDir)$(TargetName)$(TargetExt) $(SolutionDir).. 178 | 179 | 180 | 181 | 182 | CppCode 183 | %(Defines) 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /cuHarmonic/cuHarmonic.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Source Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /cuHarmonic/dummy.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/cuHarmonic/dummy.cpp -------------------------------------------------------------------------------- /cuHarmonic/mex.def: -------------------------------------------------------------------------------- 1 | LIBRARY cuHarmonic.mexw64 2 | EXPORTS 3 | 4 | mexFunction 5 | -------------------------------------------------------------------------------- /cuHarmonic/mexCUHarmonic.cu: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include "cuHarmonicDeform.cuh" 8 | 9 | 10 | template 11 | R getFieldValueWithDefault(const mxArray* mat, const char* name, R defaultvalue) 12 | { 13 | const mxArray *f = mat?mxGetField(mat, 0, name):nullptr; 14 | return f ? R(mxGetScalar(f)) : defaultvalue; 15 | } 16 | 17 | void mexError(const std::string& error) 18 | { 19 | mexErrMsgTxt(("invalid input to mex: " + error).c_str()); 20 | } 21 | 22 | const char* matClassNames[] = { "unknown", "cell", "struct", "logical", "char", "void", "double", "single", "int8", "uint8", 23 | "int16", "uint16", "int32", "uint32", "int64", "uint64", "function", "opaque", "object" /* keep the last real item in the list */ 24 | }; 25 | 26 | const mxGPUArray* getFieldGPUArray(const mxArray* mat, const char* name, mxClassID verify_mxClassID, mxComplexity verify_complexity) 27 | { 28 | const mxArray *m = mat ? mxGetField(mat, 0, name) : nullptr; 29 | 30 | if (!m) return nullptr; 31 | 32 | // has the field but not GPUArray 33 | if ( !mxIsGPUArray(m) ) mexError(std::string("cannot extract ") + name + ", not GPUArray?"); 34 | 35 | if ( mxIsCell(m) ) mexError(name + std::string(" must be an array instead of cell!")); 36 | 37 | const mxGPUArray *data = mxGPUCreateFromMxArray(m); 38 | if (mxGPUGetClassID(data) != verify_mxClassID) mexError(std::string("input array: ") + name + " should be of the same precision type as D2: " + matClassNames[verify_mxClassID]); 39 | if (mxGPUGetComplexity(data) != verify_complexity) mexError(std::string("input array: ") + name + " should be " + (verify_complexity==mxCOMPLEX?"complex":"real")); 40 | 41 | return data; 42 | } 43 | 44 | 45 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, mxArray const *prhs[]) 46 | { 47 | // interface: cuHarmonic( D2, C2, bP2P, lambda, phipsyIters, cageOffsets, params ); 48 | // params: optional, includes, isoetype, isoepow, nIter, enEvalsPerKernel, optimizationMethod 49 | 50 | mxInitGPU(); // Initialize the MathWorks GPU API. 51 | 52 | if (nrhs < 5) mexError("not enough input"); 53 | if (nrhs > 6 && !mxIsStruct(prhs[6])) mexError("params must be a struct"); 54 | 55 | /* Throw an error if the input is not a GPU array. */ 56 | if (!mxIsGPUArray(prhs[0]) || !mxIsGPUArray(prhs[1]) || !mxIsGPUArray(prhs[2]) || !mxIsGPUArray(prhs[4])) 57 | mexError("input must be GPU arrays"); 58 | 59 | mxGPUArray const *bP2P = mxGPUCreateFromMxArray(prhs[2]); // P2P target 60 | 61 | if (mxGPUGetNumberOfElements(bP2P) == 0) { 62 | mexWarnMsgTxt("No P2P is defined, solution is any global translation."); 63 | mxGPUDestroyGPUArray(bP2P); 64 | 65 | plhs[0] = mxDuplicateArray(prhs[4]); 66 | if (nlhs > 1) plhs[1] = mxCreateDoubleScalar(0); 67 | 68 | return; 69 | } 70 | 71 | mxGPUArray const *D2 = mxGPUCreateFromMxArray(prhs[0]); // derivative of Cauchy coordinates at iso energy samples 72 | mxGPUArray const *C2 = mxGPUCreateFromMxArray(prhs[1]); // Cauchy coordinates for P2P constraints 73 | const double lambda = mxGetScalar(prhs[3]); 74 | mxGPUArray *const phipsyIters = mxGPUCopyFromMxArray(prhs[4]); 75 | 76 | std::vector cageOffsets; 77 | if (nrhs > 5) { 78 | if (mxIsSparse(prhs[5]) || mxIsGPUArray(prhs[5]) || mxGetClassID(prhs[5]) != mxINT32_CLASS) 79 | mexError("input: cageoffset must be cpu array of int32 type"); 80 | 81 | std::copy_n((int*)mxGetData(prhs[5]), mxGetNumberOfElements(prhs[5]), std::back_inserter(cageOffsets)); 82 | } 83 | 84 | // all parameters 85 | const mxArray *params = nrhs>6?prhs[6]:nullptr; 86 | const int isoetype = getFieldValueWithDefault(params, "isometric_energy_type", ISO_ENERGY_SYMMETRIC_DIRICHLET); 87 | const double isoepow = getFieldValueWithDefault(params, "isometric_energy_power", 1); 88 | const int nIter = getFieldValueWithDefault(params, "nIter", 1); 89 | const int enEvalsPerKernel = getFieldValueWithDefault(params, "LS_energy_eval_per_kernel", 1); 90 | const int optimizationMethod = getFieldValueWithDefault(params, "solver", OM_NEWTON_SPDH); 91 | const int reportIterStats = getFieldValueWithDefault(params, "reportIterationStats", 1); 92 | const int linearSolvePref = getFieldValueWithDefault(params, "linearSolvePref", LINEAR_SOLVE_PREFER_CHOLESKY); 93 | const double deltaFixSPDH = getFieldValueWithDefault(params, "deltaFixSPDH", 1e-15); 94 | 95 | // Verify that invM really is a double array before extracting the pointer. 96 | if (mxGPUGetComplexity(D2) != mxCOMPLEX || mxGPUGetComplexity(C2) != mxCOMPLEX 97 | || mxGPUGetComplexity(bP2P) != mxCOMPLEX || mxGPUGetComplexity(phipsyIters) != mxCOMPLEX) 98 | mexError("input array must be in complex numbers"); 99 | 100 | const mwSize *dims = mxGPUGetDimensions(D2); // mxGPUGetNumberOfDimensions(D2) == 2; 101 | const int m = int(dims[0]); // # samples 102 | const int n = int(dims[1]); // dim Cauchy coordinates 103 | dims = mxGPUGetDimensions(C2); 104 | const int nP2P = int(dims[0]); 105 | 106 | const mxClassID runPrecision = mxGPUGetClassID(D2); 107 | 108 | 109 | const mxGPUArray *hessian_samples = getFieldGPUArray(params, "hessian_samples", mxINT32_CLASS, mxREAL); 110 | const int mh = hessian_samples? mxGPUGetNumberOfElements(hessian_samples):m; 111 | assert(mh <= m); 112 | 113 | ////////////////////////////////////////////////////////////////////////// 114 | const mxGPUArray *sample_spacings = getFieldGPUArray(params, "sample_spacings_half", runPrecision, mxREAL); 115 | const mxGPUArray* validationData_v = getFieldGPUArray(params, "v", runPrecision, mxCOMPLEX); 116 | const mxGPUArray* validationData_E2 =getFieldGPUArray(params, "E2", runPrecision, mxCOMPLEX); 117 | const mxGPUArray* validationData_L = getFieldGPUArray(params, "L", runPrecision, mxREAL); 118 | const mxGPUArray* validationData_nextSample = getFieldGPUArray(params, "nextSampleInSameCage", mxINT32_CLASS, mxREAL); 119 | 120 | // Now that we have verified the data type, extract a pointer to the input data on the device. 121 | 122 | const mxClassID inputPrecisonTypes[] = { mxGPUGetClassID(C2), mxGPUGetClassID(bP2P), mxGPUGetClassID(phipsyIters) }; 123 | for (auto pt:inputPrecisonTypes) if (pt != runPrecision) mexError("input arrays should be all of the same precision type"); 124 | 125 | const int *hessian_samples_rawp = hessian_samples ? (const int *)(mxGPUGetDataReadOnly(hessian_samples)) : nullptr; 126 | const void *sample_spacings_rawp = sample_spacings ? mxGPUGetDataReadOnly(sample_spacings) : 0; 127 | 128 | std::vector allStats; 129 | if (runPrecision == mxDOUBLE_CLASS){ 130 | using Complex = cuDoubleComplex; 131 | using real = double; 132 | 133 | HarmonicMapValidationInput validation_data; 134 | 135 | if (validationData_v && validationData_E2 && validationData_L) { 136 | validation_data = { (const Complex*)mxGPUGetDataReadOnly(validationData_v), 137 | (const Complex*)mxGPUGetDataReadOnly(validationData_E2), 138 | (const real*)mxGPUGetDataReadOnly(validationData_L), 139 | (const int*)mxGPUGetDataReadOnly(validationData_nextSample) }; 140 | } 141 | 142 | ////////////////////////////////////////////////////////////////////////// 143 | allStats = cuHarmonicDeform((Complex const *)(mxGPUGetDataReadOnly(D2)), 144 | (Complex const *)(mxGPUGetDataReadOnly(C2)), 145 | (Complex const *)(mxGPUGetDataReadOnly(bP2P)), 146 | (Complex *)(mxGPUGetData(phipsyIters)), 147 | hessian_samples_rawp, 148 | m, mh, n, cageOffsets, nP2P, isoetype, isoepow, lambda, nIter, 149 | validation_data, 150 | (const real*)sample_spacings_rawp, 151 | enEvalsPerKernel, optimizationMethod, 152 | reportIterStats, linearSolvePref, deltaFixSPDH); 153 | 154 | } 155 | else if (runPrecision == mxSINGLE_CLASS) { 156 | using Complex = cuFloatComplex; 157 | using real = float; 158 | 159 | HarmonicMapValidationInput validation_data; 160 | 161 | if (validationData_v && validationData_E2 && validationData_L) { 162 | validation_data = { (const Complex*)mxGPUGetDataReadOnly(validationData_v), 163 | (const Complex*)mxGPUGetDataReadOnly(validationData_E2), 164 | (const real*)mxGPUGetDataReadOnly(validationData_L), 165 | (const int*)mxGPUGetDataReadOnly(validationData_nextSample) }; 166 | } 167 | 168 | ////////////////////////////////////////////////////////////////////////// 169 | allStats = cuHarmonicDeform((Complex const *)(mxGPUGetDataReadOnly(D2)), 170 | (Complex const *)(mxGPUGetDataReadOnly(C2)), 171 | (Complex const *)(mxGPUGetDataReadOnly(bP2P)), 172 | (Complex *)(mxGPUGetData(phipsyIters)), 173 | hessian_samples_rawp, 174 | m, mh, n, cageOffsets, nP2P, isoetype, isoepow, lambda, nIter, 175 | validation_data, 176 | (const real*)sample_spacings_rawp, 177 | enEvalsPerKernel, optimizationMethod, 178 | reportIterStats, linearSolvePref, deltaFixSPDH); 179 | } 180 | 181 | // Wrap the result up as a MATLAB gpuArray for return. 182 | plhs[0] = mxGPUCreateMxArrayOnGPU(phipsyIters); 183 | if (nlhs > 1) { 184 | if (allStats.size() == 1) 185 | plhs[1] = mxCreateDoubleScalar(allStats[0]); 186 | else { 187 | plhs[1] = mxCreateDoubleMatrix(allStats.size() / (nIter+1), (nIter+1), mxREAL); 188 | std::copy_n(allStats.cbegin(), mxGetNumberOfElements(plhs[1]), mxGetPr(plhs[1])); 189 | } 190 | } 191 | 192 | // * The mxGPUArray pointers are host-side structures that refer to device 193 | // * data. These must be destroyed before leaving the MEX function. 194 | mxGPUDestroyGPUArray(D2); 195 | mxGPUDestroyGPUArray(C2); 196 | mxGPUDestroyGPUArray(bP2P); 197 | mxGPUDestroyGPUArray(phipsyIters); 198 | 199 | if (hessian_samples) mxGPUDestroyGPUArray(hessian_samples); 200 | if (sample_spacings) mxGPUDestroyGPUArray(sample_spacings); 201 | 202 | if (validationData_v) mxGPUDestroyGPUArray(validationData_v); 203 | if (validationData_E2) mxGPUDestroyGPUArray(validationData_E2); 204 | if (validationData_L) mxGPUDestroyGPUArray(validationData_L); 205 | if (validationData_nextSample) mxGPUDestroyGPUArray(validationData_nextSample); 206 | } 207 | -------------------------------------------------------------------------------- /data/Archery/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/Archery/data.mat -------------------------------------------------------------------------------- /data/Archery/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/Archery/image.png -------------------------------------------------------------------------------- /data/annulus/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/annulus/data.mat -------------------------------------------------------------------------------- /data/annulus/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/annulus/image.png -------------------------------------------------------------------------------- /data/colorbar/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/colorbar/data.mat -------------------------------------------------------------------------------- /data/colorbar/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/colorbar/image.png -------------------------------------------------------------------------------- /data/deer/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/deer/data.mat -------------------------------------------------------------------------------- /data/deer/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/deer/image.png -------------------------------------------------------------------------------- /data/dragon/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/dragon/data.mat -------------------------------------------------------------------------------- /data/dragon/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/dragon/image.png -------------------------------------------------------------------------------- /data/elephant/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/elephant/data.mat -------------------------------------------------------------------------------- /data/elephant/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/elephant/image.png -------------------------------------------------------------------------------- /data/frog/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/frog/data.mat -------------------------------------------------------------------------------- /data/frog/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/frog/image.png -------------------------------------------------------------------------------- /data/giraffe/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/giraffe/data.mat -------------------------------------------------------------------------------- /data/giraffe/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/giraffe/image.png -------------------------------------------------------------------------------- /data/horse/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/horse/data.mat -------------------------------------------------------------------------------- /data/horse/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/horse/image.png -------------------------------------------------------------------------------- /data/monkey/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/monkey/data.mat -------------------------------------------------------------------------------- /data/monkey/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/monkey/image.png -------------------------------------------------------------------------------- /data/racoon/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/racoon/data.mat -------------------------------------------------------------------------------- /data/racoon/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/racoon/image.png -------------------------------------------------------------------------------- /data/raptor/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/raptor/data.mat -------------------------------------------------------------------------------- /data/raptor/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/raptor/image.png -------------------------------------------------------------------------------- /data/rex/data.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/rex/data.mat -------------------------------------------------------------------------------- /data/rex/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/data/rex/image.png -------------------------------------------------------------------------------- /deformer_main.m: -------------------------------------------------------------------------------- 1 | fC2R = @(x) [real(x) imag(x)]; 2 | fR2C = @(x) complex(x(:,1), x(:,2)); 3 | 4 | %% 5 | if ~exist('working_dataset', 'var') || isempty(working_dataset) 6 | working_dataset = 'rex'; 7 | fprintf('working shape default: %s\n', working_dataset); 8 | end 9 | 10 | datadir = fullfile(cd, 'data', working_dataset, '\'); 11 | 12 | if exist('numMeshVertex', 'var')~=1 13 | numMeshVertex = 10000; 14 | fprintf('numMeshVertex default: %d\n', numMeshVertex); 15 | end 16 | 17 | 18 | 19 | datafile = fullfile(datadir, 'data.mat'); 20 | imgfilepath = fullfile(datadir, 'image.png'); 21 | 22 | P2Psrc = zeros(0,1); P2Pdst = zeros(0,1); 23 | 24 | if exist(datafile, 'file') == 2 25 | %% load presaved data 26 | load(datafile); 27 | else 28 | % read image dimension 29 | iminfo = imfinfo(imgfilepath); 30 | img_w = iminfo.Width; 31 | img_h = iminfo.Height; 32 | 33 | %% extract cage from image, only simply-connected domain is supported 34 | offset = 10; 35 | simplify = 10; 36 | allcages = GetCompCage(imgfilepath, offset, simplify, 0, 0); 37 | end 38 | 39 | cage = allcages{1}; 40 | holes = reshape( allcages(2:end), 1, [] ); 41 | 42 | [X, T] = cdt([cage, holes], [], numMeshVertex, false); 43 | X = fR2C(X); 44 | 45 | %% load p2p 46 | P2PVtxIds = triangulation(T, fC2R(X)).nearestNeighbor( fC2R(P2Psrc) ); 47 | P2PCurrentPositions = P2Pdst; 48 | iP2P = 1; 49 | 50 | %% texture 51 | uv = fR2C([real(X)/img_w imag(X)/img_h])*100 + complex(0.5, 0.5); 52 | 53 | %% solvers & energies 54 | harmonic_map_solvers = {'AQP', 'SLIM', 'Newton', 'Newton_SPDH', 'Newton_SPDH_FullEig', 'Gradient Descent', 'LBFGS', ... 55 | 'cuGD', 'cuNewton', 'cuNewton_SPDH', 'cuNewton_SPDH_FullEig'}; 56 | 57 | if hasGPUComputing 58 | default_harmonic_map_solver = 'cuNewton_SPDH'; 59 | else 60 | warning('no cuda capable GPU present, switching to CPU solver'); 61 | default_harmonic_map_solver = 'Newton_SPDH'; 62 | end 63 | 64 | harmonic_map_energies = {'SymmDirichlet', 'Exp_SymmDirichlet', 'AMIPS'}; 65 | default_harmonic_map_energy = 'SymmDirichlet'; 66 | -------------------------------------------------------------------------------- /derivativesOfCauchyCoord.m: -------------------------------------------------------------------------------- 1 | function [D, E] = derivativesOfCauchyCoord(cage, z, holeCenters) 2 | % Compute first and second derivatives of regular Cauchy coordiantes 3 | % 4 | % Input parameters: 5 | % cage - cage 6 | % z - points inside the cage 7 | 8 | z = reshape(z, [], 1); 9 | 10 | remove2coeffAtHoles = true; 11 | 12 | if iscell(cage) 13 | if nargout>1 14 | [D, E] = cellfun(@(c) derivativesOfCauchyCoord(c, z, holeCenters), cage, 'UniformOutput', false); 15 | E = cat(2, E{:}); 16 | else 17 | D = cellfun(@(c) derivativesOfCauchyCoord(c, z, holeCenters), cage, 'UniformOutput', false); 18 | end 19 | 20 | D = cat(2, D{:}); 21 | 22 | if numel(cage)>1 23 | % remove 2 holomorphic DOF for each hole 24 | if remove2coeffAtHoles 25 | cageSizes = cellfun(@numel, cage); 26 | flags = true(sum(cageSizes), 1); 27 | flags( reshape(cumsum(cageSizes(1:end-1)),[],1) + [1 2] ) = false; 28 | D = D(:, flags); 29 | if nargout>1, E = E(:, flags); end 30 | end 31 | 32 | assert(nargin>2 && ~isempty(holeCenters)); 33 | 34 | holeCenters = reshape(holeCenters, 1, []); 35 | D = [D 1./(z-holeCenters)]; 36 | 37 | if nargout>1, E = [E -(z-holeCenters).^-2]; end 38 | end 39 | 40 | else 41 | 42 | 43 | Aj = cage - cage([end 1:end-1], :); 44 | Ajp = Aj([2:end 1], :); 45 | 46 | Bj = bsxfun(@minus, cage.', z); 47 | Bjp = Bj(:, [2:end 1]); 48 | Bjm = Bj(:, [end 1:end-1]); 49 | 50 | 51 | oneOver2pi_i = 1/(2*pi*1i); 52 | 53 | D = oneOver2pi_i*(bsxfun(@rdivide, log(Bj./Bjm), Aj.') - bsxfun(@rdivide, log(Bjp./Bj), Ajp.')); 54 | 55 | if(nargout > 1) 56 | E = oneOver2pi_i*(Bjp-Bjm)./(Bjm.*Bj.*Bjp); 57 | end 58 | 59 | end 60 | -------------------------------------------------------------------------------- /distancePointToSegment.m: -------------------------------------------------------------------------------- 1 | function d = distancePointToSegment(p, v1, v2) 2 | 3 | complexDot = @(z1, z2) real(z1.*conj(z2)); 4 | 5 | t = complexDot(v2-v1, p-v1) ./ complexDot(v2-v1, v2-v1); 6 | 7 | d = abs(p-((1-t).*v1 + t.*v2)); 8 | 9 | d_t0 = abs(p-v1); 10 | d_t1 = abs(p-v2); 11 | 12 | d(t<=0) = d_t0(t<=0); 13 | d(t>=1) = d_t1(t>=1); -------------------------------------------------------------------------------- /glidviewer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/glidviewer.exe -------------------------------------------------------------------------------- /harmonicMapIsometryicEnergy.m: -------------------------------------------------------------------------------- 1 | function [e, g, h] = harmonicMapIsometryicEnergy(D, phi, psy, SPDHessian, energy_type, energy_param) 2 | 3 | if nargin<4, SPDHessian = true; end 4 | if nargin<5, energy_type = 'SymmDirichlet'; end 5 | if nargin<6, energy_param = 1; end 6 | 7 | fz2 = abs(D*phi).^2; 8 | gz2 = abs(D*psy).^2; 9 | 10 | s = energy_param; 11 | 12 | switch energy_type 13 | case 'SymmDirichlet' 14 | evec = abs( (fz2 + gz2).*(1+(fz2 - gz2).^-2) ); 15 | case 'Exp_SymmDirichlet' 16 | evec = exp( s*(fz2 + gz2).*(1+(fz2 - gz2).^-2) ); 17 | case 'AMIPS' 18 | evec = (s*2*(fz2+gz2)+1)./(fz2-gz2) + fz2-gz2; 19 | evec = exp(evec); 20 | otherwise 21 | warning('Unexpected energy type: %s. ', energy_type); 22 | end 23 | 24 | e = sum(evec); 25 | 26 | if nargout>1 27 | fz = D*phi; 28 | gz = D*psy; 29 | n = size(D,1); 30 | 31 | switch energy_type 32 | case 'SymmDirichlet' 33 | alphas = [1-((fz2-gz2).^-3).*(fz2+3*gz2) 1+((fz2-gz2).^-3).*(3*fz2+gz2)]; 34 | case 'Exp_SymmDirichlet' 35 | alphas = [1-((fz2-gz2).^-3).*(fz2+3*gz2) 1+((fz2-gz2).^-3).*(3*fz2+gz2)].*evec*s; 36 | case 'AMIPS' 37 | alphas = [1-(4*s*gz2+1).*(fz2-gz2).^-2 -(1-(4*s*fz2+1).*(fz2-gz2).^-2)]; 38 | alphas = alphas.*evec; 39 | end 40 | 41 | g = 2*[D'*(fz.*alphas(:,1)); conj(D'*(gz.*alphas(:,2)))]; 42 | end 43 | 44 | if nargout>2 45 | m = size(D,2); 46 | 47 | switch energy_type 48 | case 'SymmDirichlet' 49 | betas = 2*[fz2+5*gz2 5*fz2+gz2 -3*(fz2+gz2)].*(fz2-gz2).^-4; 50 | case 'Exp_SymmDirichlet' 51 | betas = 2*[fz2+5*gz2 5*fz2+gz2 -3*(fz2+gz2)].*(fz2-gz2).^-4; 52 | betas = alphas(:,[1 2 1]).*alphas(:,[1 2 2])./evec + betas.*evec*s; 53 | case 'AMIPS' 54 | betas = 2*[4*s*gz2+1 4*s*fz2+1].*(fz2-gz2).^-3; 55 | betas(:,3) = -mean(betas,2); 56 | betas = alphas(:,[1 2 1]).*alphas(:,[1 2 2])./evec + betas.*evec; 57 | end 58 | 59 | if SPDHessian 60 | switch energy_type 61 | case {'SymmDirichlet', 'Exp_SymmDirichlet'} 62 | i = alphas(:,1)<0; 63 | betas(i,1) = betas(i,1) + alphas(i,1)/2./fz2(i); 64 | alphas(i,1) = 0; 65 | otherwise 66 | s1s2 = (alphas+2*betas(:,1:2).*[fz2 gz2])*[1 1; 1 -1]; 67 | lambda34 = [s1s2(:,1) sqrt(s1s2(:,2).^2+16*betas(:,3).^2.*fz2.*gz2)]*[1 1; 1 -1]; 68 | t1t2 = (lambda34 - 2*alphas(:,1) - 4*betas(:,1).*fz2)./(4*betas(:,3).*gz2); 69 | eigenvec34nrm2 = fz2+gz2.*t1t2.^2; 70 | lambda34 = max(lambda34, 0)./eigenvec34nrm2; 71 | 72 | i = gz2>1e-50; 73 | alphas(i,:) = max(alphas(i,:), 0)*4; 74 | betas(i,:) = [sum( lambda34(i,:), 2 )-alphas(i,1)/2./fz2(i) ... 75 | sum( lambda34(i,:).*t1t2(i,:).^2, 2 )-alphas(i,2)/2./gz2(i) ... 76 | sum( lambda34(i,:).*t1t2(i,:), 2 )]; 77 | 78 | end 79 | end 80 | 81 | %% 82 | ss1 = [real(fz).^2; real(fz).*imag(fz); real(fz).*imag(fz); imag(fz).^2].*repmat(betas(:,1),4,1) + [alphas(:,1); zeros(n*2,1); alphas(:,1)]*.5; 83 | ss2 = [real(gz).^2; real(gz).*imag(gz); real(gz).*imag(gz); imag(gz).^2].*repmat(betas(:,2),4,1) + [alphas(:,2); zeros(n*2,1); alphas(:,2)]*.5; 84 | ss3 = [real(fz).*real(gz); real(fz).*imag(gz); imag(fz).*real(gz); imag(fz).*imag(gz)].*repmat(betas(:,3),4,1); 85 | 86 | %% 87 | DR = [real(D) -imag(D)]; 88 | DI = [imag(D) real(D)]; 89 | DRDI = [DR; DI]; 90 | n = size(D,1); 91 | 92 | if isa(D, 'gpuArray') 93 | h = gpuArray.zeros(m*4); 94 | else 95 | h = zeros(m*4); 96 | end 97 | 98 | h(1:2*m, 1:2*m) = DRDI'*[ DR.*ss1(1:n)+DI.*ss1(n+1:2*n); DR.*ss1(2*n+1:3*n)+DI.*ss1(3*n+1:4*n) ]; 99 | h(end/2+1:end, end/2+1:end) = DRDI'*[ DR.*ss2(1:n)+DI.*ss2(n+1:2*n); DR.*ss2(2*n+1:3*n)+DI.*ss2(3*n+1:4*n) ]; 100 | 101 | h(1:2*m, end/2+1:end) = DRDI'*[ DR.*ss3(1:n)+DI.*ss3(n+1:2*n); DR.*ss3(2*n+1:3*n)+DI.*ss3(3*n+1:4*n) ]; 102 | h(end/2+1:end, 1:2*m) = h(1:2*m, end/2+1:end)'; 103 | 104 | RIRI2RRII = [1:m m*2+(1:m) m+(1:m) m*3+(1:m)]; 105 | h = h(RIRI2RRII, RIRI2RRII)*4; 106 | end 107 | -------------------------------------------------------------------------------- /hasGPUComputing.m: -------------------------------------------------------------------------------- 1 | function r = hasGPUComputing() 2 | 3 | % r = false; return; 4 | 5 | persistent hasgpu; 6 | if isempty(hasgpu), hasgpu = gpuDeviceCount>0; end 7 | 8 | r = hasgpu; -------------------------------------------------------------------------------- /lbfgs_iter.m: -------------------------------------------------------------------------------- 1 | function [r,convergeflag] = lbfgs_iter(invH0, g, x) 2 | 3 | k = size(x,2); 4 | s = x(:,1:end-1) - x(:,2:end); 5 | t = g(:,1:end-1) - g(:,2:end); 6 | rho = dot(s, t); 7 | 8 | 9 | if isa(g, 'gpuArray') 10 | alpha = gpuArray.zeros(k-1, 1); 11 | else 12 | alpha = zeros(k-1, 1); 13 | end 14 | 15 | 16 | q = -g(:,1); 17 | for i=1:k-1 18 | if rho(i)==0; break; end % converged? or LBFGS not quite work, switch to gradient descent by break here 19 | alpha(i) = dot(s(:,i),q)/rho(i); 20 | q = q - alpha(i)*t(:,i); 21 | end 22 | 23 | r = invH0*q; 24 | 25 | for i=k-1:-1:1 26 | if rho(i)==0; break; end 27 | beta = dot(t(:,i), r)/rho(i); 28 | r = r + s(:,i)*(alpha(i)-beta); 29 | end 30 | 31 | -------------------------------------------------------------------------------- /lineSearchLocallyInjectiveHarmonicMap.m: -------------------------------------------------------------------------------- 1 | function [ls_t, minGlobal_sigma2] = lineSearchLocallyInjectiveHarmonicMap(phipsy, dpp, fzgz0, dfzgz, ls_t, fillDistanceSegments, v, E2, L, nextSampleInSameCage) 2 | 3 | %% compute second order differentail first, with reduced variable set for multiply connected domains 4 | fzzgzz = E2*phipsy; 5 | dfzzgzz = E2*dpp; 6 | 7 | %% 8 | if iscell(v) 9 | %% special process for holes 10 | cageSizes = cellfun(@numel, v); 11 | inextv = [2:sum(cageSizes) 1]; 12 | iprevv = inextv([end-1:end 1:end-2]); 13 | inextv( cumsum(cageSizes) ) = 1+cumsum([0 cageSizes(1:end-1)]); 14 | iprevv( 1+cumsum([0 cageSizes(1:end-1)]) ) = cumsum(cageSizes); 15 | v = cat(1, v{:}); 16 | 17 | nVarAll = numel(v)+numel(cageSizes)-1; 18 | isFreeVar = ~full( sparse(1, reshape( cumsum(cageSizes(1:end-1)), [], 1 ) + [1 2], true, 1, nVarAll) ); 19 | mfree2full = full( sparse(find(isFreeVar), 1:sum(isFreeVar), 1, nVarAll, size(phipsy,1)) ); 20 | 21 | phipsy = mfree2full*phipsy; 22 | dpp = mfree2full*dpp; 23 | 24 | assert( nargin>9 ); 25 | else 26 | inextv = [2:size(phipsy, 1) 1]; % next phipsy in the same cage 27 | iprevv = inextv([end-1:end 1:end-2]); % previous phipsy in the same cage 28 | nextSampleInSameCage = [2:size(fzgz0,1) 1]; 29 | end 30 | 31 | normdpp = norm(dpp); 32 | 33 | %% non-vanishing fz 34 | % this check combined with the condition that min(|fz|)>0 for all segments is sufficient condition to assure that fz does not vanish inside the domain 35 | % proof in the paper is slightly weaker, as it requires max(|fz0|, |fz1|) > L_fz*len 36 | argument_princial_approx = inf; 37 | while ls_t*normdpp>1e-20 38 | fzgzt = fzgz0 + ls_t*dfzgz; 39 | 40 | dtheta = angle( fzgzt(nextSampleInSameCage,1)./fzgzt(:,1) ); 41 | argument_princial_approx = sum( dtheta ); 42 | if argument_princial_approx < 1 43 | break; 44 | end 45 | 46 | ls_t = ls_t/2; 47 | end 48 | 49 | %% 50 | fAvgOverSamplePairs = @(x) (x+x(nextSampleInSameCage,:))/2; 51 | fAvgAbsOverSamplePairs = @(x) fAvgOverSamplePairs( abs(x) ); 52 | fDiff = @(x) x(inextv, :) - x; 53 | fDiffP = @(x) x - x(iprevv, :); 54 | 55 | 56 | nv = numel(v); 57 | dS = fDiffP( fDiff(phipsy(1:nv,:))./fDiff(v) ); 58 | ddS = fDiffP( fDiff( dpp(1:nv,:))./fDiff(v) ); 59 | 60 | % add Lipschitz for holes 61 | logTermIdxs = nv+1:size(phipsy,1); 62 | if ~isempty(logTermIdxs) 63 | dS = [dS; phipsy(logTermIdxs,:)]; 64 | ddS= [ddS; dpp(logTermIdxs,:)]; 65 | end 66 | 67 | delta_L_fzgz0 = L*abs(dS); 68 | delta_L_fzgz1 = L*abs(dS+ls_t*ddS); 69 | ls_t0 = ls_t; 70 | 71 | minGlobal_sigma2 = -inf; 72 | 73 | 74 | while true 75 | sufficientstep = ls_t*normdpp>1e-12; 76 | 77 | % L_fzgz = fAvgAbsOverPairs(fzzgzz+ls_t*dfzzgzz) + (L*abs(dSdz+ls_t*ddSdz)).*fillDistanceSegments; 78 | 79 | delta_L_fzgz = delta_L_fzgz0 + (delta_L_fzgz1-delta_L_fzgz0)*(ls_t/ls_t0); 80 | L_fzgz = fAvgAbsOverSamplePairs(fzzgzz+ls_t*dfzzgzz) + delta_L_fzgz.*fillDistanceSegments; 81 | fzgzt = fzgz0 + ls_t*dfzgz; 82 | 83 | % fprintf('L_fz: %.2f, L_fzbar: %.2f\n', max(L_fzgz)); 84 | 85 | %% local injectivity, sigma2>0 86 | min_absfz = fAvgAbsOverSamplePairs( fzgzt(:,1) ) - L_fzgz(:,1).*fillDistanceSegments; 87 | max_absgz = fAvgAbsOverSamplePairs( fzgzt(:,2) ) + L_fzgz(:,2).*fillDistanceSegments; 88 | minGlobal_sigma2 = min(min_absfz - max_absgz); 89 | 90 | if sufficientstep && minGlobal_sigma2 < 0 91 | ls_t = ls_t/2; 92 | continue; 93 | end 94 | 95 | max_absfz = fAvgAbsOverSamplePairs( fzgzt(:,1) ) + L_fzgz(:,1).*fillDistanceSegments; 96 | maxGlobal_sigma1 = max(max_absfz + max_absgz); 97 | maxGlobal_k = max( max_absgz ./ min_absfz ); 98 | 99 | maxOnSamples_sigma1 = max(abs(fzgzt)*[1; 1]); 100 | minOnSamples_sigma2 = min(abs(fzgzt)*[1; -1]); 101 | maxOnSamples_k = max( abs(fzgzt(:,2))./abs(fzgzt(:,1)) ); 102 | 103 | 104 | fprintf('t: %.5f, sigma1: (%.3f, %.3f), sigma2: (%.3f, %.3f), k: (%.3f, %.3f)\n', ls_t, maxOnSamples_sigma1, maxGlobal_sigma1, minOnSamples_sigma2, minGlobal_sigma2, maxOnSamples_k, maxGlobal_k); 105 | % assert(maxGlobal_sigma1>0); 106 | 107 | %% all condition satisfied or step too small 108 | break; 109 | end 110 | 111 | % argument principal must be satisfied: contour integral fz'/fz = 0 <=> fz non vanishing 112 | % assert( argument_princial_approx < 1 ); 113 | -------------------------------------------------------------------------------- /list_datasets.m: -------------------------------------------------------------------------------- 1 | 2 | datadirs = dir('data'); 3 | isdataset = arrayfun(@(i) datadirs(i).isdir && datadirs(i).name(1)~='.', 1:numel(datadirs)); 4 | datasets = {datadirs(isdataset).name}; 5 | -------------------------------------------------------------------------------- /maxtForPhiPsy.m: -------------------------------------------------------------------------------- 1 | function t = maxtForPhiPsy( fz, gz, dfz, dgz ) 2 | 3 | a = abs(dfz)^2 - abs(dgz)^2; 4 | b = 2*real( conj(fz)*dfz - conj(gz)*dgz ); 5 | c = abs(fz)^2-abs(gz)^2; 6 | 7 | % assert( all( c>=-1e-10 ) ); 8 | 9 | delta = b^2 - 4*a*c; 10 | 11 | 12 | % t = single(inf); 13 | t = inf; 14 | if ~(a>0 && (delta<0 || b>0)) 15 | t = (-b-sqrt(delta))/a/2; 16 | end 17 | 18 | % assert( t>=0 ); 19 | t = max(t, 0); 20 | -------------------------------------------------------------------------------- /maxtForPositiveArea.m: -------------------------------------------------------------------------------- 1 | function t = maxtForPositiveArea(e0, e1) 2 | 3 | if ~isreal(e0), e0 = [real(e0) imag(e0)]; end 4 | if ~isreal(e1), e1 = [real(e1) imag(e1)]; end 5 | 6 | A0 = e0(:, 1).*e0(:,4) - e0(:, 2).*e0(:,3); 7 | A1 = e1(:, 1).*e1(:,4) - e1(:, 2).*e1(:,3); 8 | B = dot(e0, [1 -1 -1 1].*e1(:,[4 3 2 1]), 2); 9 | 10 | a = A0 + A1 - B; 11 | b = B - 2*A0; 12 | c = A0; 13 | 14 | 15 | 16 | delta = b.^2 - 4*a.*c; 17 | t = A0*inf; 18 | 19 | % i = ~(a>0 & (delta<0 | b>0)); 20 | i = a<0 | (delta>0 & b<=0); 21 | t(i) = (-b(i)-sqrt(delta(i)))./a(i)/2; 22 | 23 | 24 | %% need to take care of special case where a==0 numerically 25 | i2 = abs(a)<1e-16 & b<0; 26 | t(i2) = -c(i2)./b(i2); 27 | 28 | assert( ~any( a<0 & delta<0 ) ); 29 | 30 | t = max(t, 0); 31 | -------------------------------------------------------------------------------- /meshAQP.m: -------------------------------------------------------------------------------- 1 | function [z, allStats, meshXs] = meshAQP(x, t, P2PVtxIds, P2PCurrentPositions, z, nIter) 2 | 3 | fC2R = @(x) [real(x) imag(x)]; 4 | fR2C = @(x) complex(x(:,1), x(:,2)); 5 | 6 | if ~exist('OptimProblemIsoDist','file') 7 | addpath( genpath([pwd '\meshAQP']) ); 8 | end 9 | 10 | tic 11 | if norm(z(P2PVtxIds) - P2PCurrentPositions)>1e-6 12 | initArapIter = 25; %1 13 | z = meshARAP(x, t, P2PVtxIds, P2PCurrentPositions, z, initArapIter); 14 | end 15 | 16 | nv = numel(x); 17 | nP2P = numel(P2PVtxIds); 18 | eq_lhs = sparse( 1:nP2P*2, [P2PVtxIds; P2PVtxIds+nv], 1, nP2P*2, nv*2 ); 19 | eq_rhs = [real(P2PCurrentPositions); imag(P2PCurrentPositions)]; 20 | 21 | optimProblem = OptimProblemIsoDist(fC2R(x), t, eq_lhs, eq_rhs, fC2R(z)); 22 | preprocessTime = toc; 23 | 24 | %% setup solver 25 | useAccelaration = true; 26 | aqpSolver = OptimSolverAcclQuadProx('AQP', optimProblem, useAccelaration, true, true); aqpSolver.setKappa(1000); 27 | 28 | %% solve 29 | TolX = 1e-10; 30 | TolFun = 1e-6; 31 | logs = aqpSolver.solveTol(TolX, TolFun, nIter); 32 | 33 | z = fR2C( logs.X(:, :, end) ); 34 | 35 | if nargout>2, meshXs = logs.X; end 36 | 37 | allStats(:, [5 8]) = [ [preprocessTime; logs.t_iter(2:end)']*1000 logs.f'/sum(signedAreas(x,t)) ]; 38 | 39 | fprintf('%dits: initilization time: %.4e, mean runtime: %.3e\n', nIter, preprocessTime*1000, mean(allStats(2:end,5))); 40 | 41 | -------------------------------------------------------------------------------- /meshAQP/code/OptimProblem.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | classdef (Abstract) OptimProblem < handle 10 | properties 11 | % problem 12 | T; 13 | eq_lhs; 14 | eq_rhs; 15 | x0; 16 | n_vars; 17 | n_eq; 18 | H; 19 | 20 | % parameters 21 | verbose = 2; 22 | end 23 | 24 | methods (Abstract) 25 | evaluateFunctional(obj, x, doVal, doGrad, doHess) 26 | setQuadraticProxy(obj) 27 | getMaxStep(obj, x, p) 28 | end 29 | 30 | methods 31 | function obj = OptimProblem 32 | end 33 | 34 | function initProblem(obj) 35 | obj.n_vars = size(obj.T,2); 36 | obj.n_eq = size(obj.eq_lhs,1); 37 | end 38 | 39 | function f = evaluateValue(obj, x) 40 | f = evaluateFunctional(obj, x, true, false, false); 41 | end 42 | function f_grad = evaluateGrad(obj, x) 43 | f_grad = evaluateFunctional(obj, x, false, true, false); 44 | end 45 | function [f, f_grad] = evaluateValueGrad(obj, x) 46 | [f, f_grad] = evaluateFunctional(obj, x, true, true, false); 47 | end 48 | 49 | function [estH,err] = estimateHessian(obj, x) 50 | [estH,err] = hessian(@(x) obj.evaluateFunctional(x, true, false, false) ,x); 51 | end 52 | 53 | function report(obj,verbosity,varargin) 54 | if verbosity<=obj.verbose 55 | fprintf(varargin{:}); 56 | end 57 | end 58 | end 59 | 60 | end -------------------------------------------------------------------------------- /meshAQP/code/OptimProblemArap.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | classdef OptimProblemArap < OptimProblem 10 | properties 11 | % mesh (keep here?) 12 | V; 13 | F; 14 | dim; 15 | n_vert; 16 | n_tri; 17 | areas; 18 | w; 19 | 20 | % internal variables 21 | Tx; 22 | R; 23 | wTxMinusR; 24 | end 25 | 26 | methods 27 | function obj = OptimProblemArap(V, F, eq_lhs, eq_rhs, V0) 28 | % copy 29 | obj.V = V; 30 | obj.F = F; 31 | obj.eq_lhs = eq_lhs; 32 | obj.eq_rhs = eq_rhs; 33 | obj.dim = size(F,2)-1; 34 | obj.n_vert = size(V,1); 35 | obj.n_tri = size(F,1); 36 | % report 37 | obj.report(1,'Constructing %s (dim: %d #vert: %d #elem: %d)\n', class(obj), obj.dim, obj.n_vert, obj.n_tri); 38 | % compute transformations 39 | [obj.T,obj.areas] = computeMeshTranformationCoeffsMex(F, V); 40 | % set weights 41 | obj.w = kron(sqrt(obj.areas), ones(obj.dim^2,1)); 42 | % set initial configuration 43 | obj.x0 = obj.initVertices(V0); 44 | % set quadratic proxy 45 | obj.H = obj.setQuadraticProxy(); 46 | % finish construction 47 | obj.initProblem(); 48 | end 49 | 50 | function x0 = initVertices(obj, V0) 51 | % feasible initialization -- provide input or a single local-global step 52 | if ~isempty(V0) 53 | x0 = V0; 54 | else 55 | obj.Tx = obj.T*obj.V(:); 56 | obj.R = projectRotationMexFast(obj.Tx, obj.dim); 57 | x0 = solveConstrainedLS(obj.T, obj.R, obj.eq_lhs, obj.eq_rhs); 58 | x0 = reshape(x0, obj.n_vert, obj.dim); 59 | end 60 | end 61 | 62 | function [varargout] = evaluateFunctional(obj, x, doVal, doGrad, doHess) 63 | assert(~doHess, 'Hessian computation is not implementated for this functional'); 64 | % evaluate 65 | obj.Tx = obj.T*x; 66 | obj.R = projectRotationMexFast(obj.Tx, obj.dim); 67 | obj.wTxMinusR = obj.w.*(obj.Tx-obj.R); 68 | n_arg = 0; 69 | if doVal 70 | n_arg = n_arg + 1; 71 | varargout{n_arg} = sum(obj.wTxMinusR.^2); 72 | end 73 | if doGrad 74 | n_arg = n_arg + 1; 75 | varargout{n_arg} = 2*((obj.w.*obj.wTxMinusR)'*obj.T)'; 76 | end 77 | end 78 | 79 | function H = setQuadraticProxy(obj) 80 | wT = spdiag(obj.w)*obj.T; 81 | H = 2*(wT'*wT); 82 | end 83 | 84 | function t_max = getMaxStep(obj, x, p) 85 | t_max = inf; % do not enforce a t_max constraint for this functional 86 | end 87 | 88 | function e = evaluatePerElementEnergy(obj, x) 89 | % evaluate 90 | obj.Tx = obj.T*x; 91 | obj.R = projectRotationMexFast(obj.Tx, obj.dim); 92 | TxMinusR = reshape(obj.Tx-obj.R, obj.dim^2, []); 93 | e = sqrt(sum(TxMinusR.^2,1))'; 94 | end 95 | end 96 | 97 | end 98 | 99 | 100 | -------------------------------------------------------------------------------- /meshAQP/code/OptimProblemIsoDist.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | classdef OptimProblemIsoDist < OptimProblem 10 | properties 11 | % mesh (keep here?) 12 | V; 13 | F; 14 | dim; 15 | n_vert; 16 | n_tri; 17 | areas; 18 | 19 | % internal variables 20 | Tx; 21 | R; 22 | f_val; 23 | Tx_grad; 24 | flips; 25 | localHess; 26 | 27 | % parameters 28 | maxStepTol = 1e-8; 29 | initArapIter; 30 | 31 | % bd projection parameters (for initialization) 32 | proj_bd_K = 10; % bounded distortion constant 33 | proj_bd_lb = -1; % lower bound on SVs (-1 = disabled) 34 | proj_bd_ub = -1; % upper bound on SVs (-1 = disabled) 35 | proj_bd_iter_max = 1000; % maximal number of BD projection iterations 36 | proj_bd_tol_err = 1e-10; % tolerance for stopping BD projection iterations 37 | proj_bd_verbose = true; % flag to showing iteration statistics 38 | end 39 | 40 | methods 41 | function obj = OptimProblemIsoDist(V, F, eq_lhs, eq_rhs, V0, initArapIter) 42 | % copy 43 | obj.V = V; 44 | obj.F = F; 45 | obj.eq_lhs = eq_lhs; 46 | obj.eq_rhs = eq_rhs; 47 | obj.dim = size(F,2)-1; 48 | obj.n_vert = size(V,1); 49 | obj.n_tri = size(F,1); 50 | if exist('initArapIter','var') 51 | obj.initArapIter = initArapIter; 52 | else 53 | obj.initArapIter = 1; 54 | end 55 | % report 56 | obj.report(1,'Constructing %s (dim: %d #vert: %d #elem: %d)\n', class(obj), obj.dim, obj.n_vert, obj.n_tri); 57 | % compute transformations 58 | [obj.T,obj.areas] = computeMeshTranformationCoeffsMex(F, V); 59 | % set initial configuration 60 | obj.x0 = obj.initVertices(V0); 61 | % set quadratic proxy 62 | obj.H = obj.setQuadraticProxy(); 63 | % finish construction 64 | obj.initProblem(); 65 | end 66 | 67 | function x0 = initVertices(obj, V0) 68 | % feasible initialization -- provide input or a single local-global step 69 | if ~isempty(V0) 70 | x0 = V0; 71 | % assert( norm(obj.eq_lhs*x0(:)-obj.eq_rhs)<1e-6, 'invalid initlization, position constraints not satisfied' ); 72 | else 73 | x0 = obj.V(:); 74 | obj.report(2,'Init with %d ARAP iterations ', obj.initArapIter); 75 | for arapIter = 1:obj.initArapIter 76 | obj.Tx = obj.T*x0; 77 | obj.R = projectRotationMexFast(obj.Tx, obj.dim); 78 | x0 = solveConstrainedLS(obj.T, obj.R, obj.eq_lhs, obj.eq_rhs); 79 | progBar; 80 | end 81 | obj.report(2,'\n'); 82 | x0 = reshape(x0, obj.n_vert, obj.dim); 83 | end 84 | % check if solution is orientation preserving 85 | obj.Tx = obj.T*x0(:); 86 | [~, ~, obj.flips] = computeFunctionalIsoDistMex(obj.Tx, obj.areas, obj.dim); 87 | % fix if there are flips 88 | if obj.flips 89 | warning('Initialization is not orientation preserving -- projecting on BD') 90 | % project onto BD 91 | solver_bd = SolverProjectorBD(obj.F, obj.V, obj.eq_lhs, obj.eq_rhs, obj.proj_bd_K, obj.proj_bd_lb, obj.proj_bd_ub, x0, SolverProjectorModeEnum.Tangent); % setup BD solver 92 | solver_bd.solve(obj.proj_bd_iter_max, obj.proj_bd_tol_err, obj.proj_bd_verbose); % solve BD projection 93 | assert(nnz(solver_bd.flips)==0, 'BD projector output has flipped elements'); 94 | x0 = solver_bd.y; % reassign x0 95 | end 96 | 97 | function progBar 98 | if obj.verbose>=1 99 | progressbar(arapIter, obj.initArapIter , 40); 100 | end 101 | end 102 | end 103 | 104 | function [varargout] = evaluateFunctional(obj, x, doVal, doGrad, doHess) 105 | % evaluate 106 | obj.Tx = obj.T*x; 107 | if doVal||doGrad 108 | % call mex helps 109 | [obj.f_val, obj.Tx_grad, obj.flips] = computeFunctionalIsoDistMex(obj.Tx, obj.areas, obj.dim); 110 | if obj.flips 111 | warning('negative det'); 112 | obj.f_val = inf; 113 | end 114 | end 115 | % return 116 | n_arg = 0; 117 | if doVal 118 | n_arg = n_arg + 1; 119 | varargout{n_arg} = obj.f_val; 120 | end 121 | if doGrad 122 | n_arg = n_arg + 1; 123 | varargout{n_arg} = (obj.Tx_grad'*obj.T)'; 124 | end 125 | if doHess 126 | n_arg = n_arg + 1; 127 | obj.localHess = computeHessianIsoDistMex(obj.Tx, obj.areas, obj.dim); 128 | varargout{n_arg} = obj.T'*obj.localHess*obj.T; 129 | end 130 | end 131 | 132 | function H = setQuadraticProxy(obj) 133 | wT = spdiag(kron(sqrt(obj.areas), ones(obj.dim^2,1)))*obj.T; 134 | H = 2*(wT'*wT); 135 | end 136 | 137 | function t_max = getMaxStep(obj, x, p) 138 | t_max = computeInjectiveStepSizeMex(obj.F,x,p,obj.maxStepTol); 139 | end 140 | 141 | function e = evaluatePerElementEnergy(obj, x) 142 | % evaluate 143 | obj.Tx = obj.T*x; 144 | A = reshape(obj.Tx, obj.dim, obj.dim, []); 145 | e = nan(obj.n_tri,1); 146 | for ii = 1:obj.n_tri 147 | e(ii) = sqrt( sum(sum(A(:,:,ii).^2)) + sum(sum(inv(A(:,:,ii)).^2)) )/sqrt(2*obj.dim); 148 | end 149 | end 150 | end 151 | 152 | end 153 | 154 | 155 | -------------------------------------------------------------------------------- /meshAQP/code/OptimSolver.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | classdef (Abstract) OptimSolver < handle 10 | properties 11 | % solver vars 12 | optimProblem; 13 | x; 14 | 15 | % solver parameters 16 | 17 | % log 18 | tag = ''; 19 | verbose = 2; 20 | 21 | % default solver parameters 22 | default_max_iter = 500; 23 | default_TolX = 1e-10; 24 | default_TolFun = 1e-6; 25 | end 26 | 27 | properties (Dependent) 28 | X; 29 | end 30 | 31 | methods (Abstract) 32 | solveTol(obj, TolX, TolFun, max_iter) 33 | end 34 | 35 | methods 36 | function obj = OptimSolver 37 | 38 | end 39 | 40 | function value = get.X(obj) 41 | value = reshape(obj.x,size(obj.optimProblem.x0)); 42 | end 43 | 44 | function initSolver(obj, tag, optimProblem) 45 | % copy 46 | obj.tag = tag; 47 | obj.optimProblem = optimProblem; 48 | obj.x = obj.optimProblem.x0(:); 49 | % report 50 | obj.report(1,'Constructing %s::%s (#var: %d)\n', obj.tag, class(obj), obj.optimProblem.n_vars); 51 | end 52 | 53 | function log = solveN(obj,num_iter) 54 | log = obj.solveTol(0, 0, num_iter); 55 | end 56 | 57 | function log = solveDefault(obj) 58 | log = obj.solveTol(obj.default_TolX, obj.default_TolFun, obj.default_max_iter); 59 | end 60 | 61 | function report(obj,verbosity,varargin) 62 | if verbosity<=obj.verbose 63 | fprintf(varargin{:}); 64 | end 65 | end 66 | end 67 | end -------------------------------------------------------------------------------- /meshAQP/code/OptimSolverAcclQuadProx.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | classdef OptimSolverAcclQuadProx < OptimSolverIterative 10 | properties 11 | % internal variables 12 | x_prev; 13 | y; 14 | p; 15 | KKT; 16 | KKT_rhs; 17 | p_lambda; 18 | y_f; 19 | y_fgrad; 20 | t_start; 21 | t_init; % store intialization time 22 | f_count = 0; % store function evaluation count 23 | 24 | % solver parameters 25 | useAccelaration; 26 | useQuadProxy; 27 | useLineSearch; 28 | theta = []; 29 | 30 | % step size limiting 31 | useAccelarationStepSizeLimit = true; 32 | accelarationStepSizeLimitFactor = 0.5; 33 | accelarationStepSize; 34 | useLineSearchStepSizeLimit = true; 35 | lineSearchStepSizeLimitFactor = 0.5; %0.99; 36 | useLineSearchStepSizeMemory = true; 37 | lineSearchStepSizeMemoryFactor = 2^5.5; 38 | 39 | % line search parameters 40 | ls_alpha = 0.2; 41 | ls_beta = 0.5; 42 | end 43 | 44 | methods 45 | function obj = OptimSolverAcclQuadProx(tag, optimProblem, useAccelaration, useQuadProxy, useLineSearch) 46 | t_init_start = tic; 47 | % copy 48 | obj.useAccelaration = useAccelaration; 49 | obj.useQuadProxy = useQuadProxy; 50 | obj.useLineSearch = useLineSearch; 51 | % init solver 52 | obj.initSolver(tag, optimProblem); 53 | % precomputaions 54 | if obj.useQuadProxy 55 | KKT_mat = [obj.optimProblem.H obj.optimProblem.eq_lhs'; obj.optimProblem.eq_lhs sparse(obj.optimProblem.n_eq,obj.optimProblem.n_eq)]; 56 | else 57 | KKT_mat = [speye(size(optimProblem.H)) obj.optimProblem.eq_lhs'; obj.optimProblem.eq_lhs sparse(obj.optimProblem.n_eq,obj.optimProblem.n_eq)]; 58 | end 59 | obj.KKT = SparseLU(KKT_mat); 60 | % init internal variables 61 | obj.KKT_rhs = zeros(obj.optimProblem.n_vars+obj.optimProblem.n_eq,1); 62 | obj.x_prev = obj.x; 63 | obj.t = 1/obj.lineSearchStepSizeMemoryFactor; 64 | % store init time 65 | obj.t_init = toc(t_init_start); 66 | end 67 | 68 | function iterate(obj) 69 | % acceleration 70 | if obj.useAccelaration 71 | if obj.useAccelarationStepSizeLimit 72 | obj.accelarationStepSize = min(obj.theta, obj.accelarationStepSizeLimitFactor*obj.optimProblem.getMaxStep(obj.x, (obj.x-obj.x_prev))); 73 | else 74 | obj.accelarationStepSize = obj.theta; 75 | end 76 | %obj.y = (1+obj.theta)*obj.x + obj.theta*obj.x_prev; 77 | obj.y = obj.x + obj.accelarationStepSize*(obj.x-obj.x_prev); 78 | else 79 | obj.y = obj.x; 80 | end 81 | 82 | % quadratic proxy minimization 83 | if obj.useLineSearch 84 | [obj.y_f, obj.y_fgrad] = obj.optimProblem.evaluateValueGrad(obj.y); 85 | obj.f_count = obj.f_count + 1; 86 | else 87 | obj.y_fgrad = obj.optimProblem.evaluateGrad(obj.y); 88 | obj.f_count = obj.f_count + 1; 89 | end 90 | obj.KKT_rhs(1:obj.optimProblem.n_vars) = -obj.y_fgrad; 91 | obj.p_lambda = obj.KKT.solve(obj.KKT_rhs); 92 | obj.p = obj.p_lambda(1:obj.optimProblem.n_vars); 93 | 94 | % initialize step size 95 | if obj.useLineSearchStepSizeMemory 96 | obj.t_start = min(obj.t * obj.lineSearchStepSizeMemoryFactor, 1); 97 | else 98 | obj.t_start = 1; 99 | end 100 | if obj.useLineSearchStepSizeLimit 101 | obj.t = min(obj.t_start, obj.lineSearchStepSizeLimitFactor*obj.optimProblem.getMaxStep(obj.y, obj.p)); 102 | else 103 | obj.t = obj.t_start; 104 | end 105 | 106 | % line search 107 | if obj.useLineSearch 108 | [linesearch_cond_lhs, linesearch_cond_rhs] = computeLineSearchCond; 109 | while linesearch_cond_lhs>linesearch_cond_rhs 110 | obj.t = obj.ls_beta*obj.t; 111 | [linesearch_cond_lhs, linesearch_cond_rhs] = computeLineSearchCond; 112 | end 113 | end 114 | % fprintf('%e %e %e\n',obj.t_start,obj.lineSearchStepSizeLimitFactor*obj.optimProblem.getMaxStep(obj.y, obj.p),obj.t) 115 | 116 | % update 117 | obj.x_prev = obj.x; 118 | obj.x = obj.y + obj.t*obj.p; 119 | 120 | function [linesearch_cond_lhs, linesearch_cond_rhs] = computeLineSearchCond 121 | linesearch_cond_lhs = obj.optimProblem.evaluateValue(obj.y + obj.t*obj.p); 122 | obj.f_count = obj.f_count + 1; 123 | linesearch_cond_rhs = obj.y_f + obj.ls_alpha*obj.t*obj.y_fgrad'*obj.p; 124 | end 125 | end 126 | 127 | function setKappa(obj, kappa) 128 | obj.theta = (1-sqrt(1/kappa))/(1+sqrt(1/kappa)); 129 | end 130 | 131 | end 132 | 133 | end -------------------------------------------------------------------------------- /meshAQP/code/OptimSolverIterative.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | classdef (Abstract) OptimSolverIterative < OptimSolver 10 | properties 11 | % solver vars 12 | t = nan; 13 | 14 | % 15 | stopCntAccept = 5; 16 | tolXCnt = 0; 17 | tolFunCnt = 0; 18 | 19 | end 20 | 21 | properties (Dependent) 22 | end 23 | 24 | methods (Abstract) 25 | iterate(obj) 26 | end 27 | 28 | methods 29 | function obj = OptimSolverIterative 30 | end 31 | 32 | function log = solveTol(obj, TolX, TolFun, max_iter) 33 | obj.report(1,'Solving %s::%s (TolX: %f TolFun: %f #max iter: %d) ', obj.tag, class(obj), TolX, TolFun, max_iter); 34 | logInit; 35 | logState; 36 | % run num_iter iteration 37 | for iter = 1:max_iter 38 | t_iter_start = tic; 39 | obj.iterate(); 40 | t_iter = toc(t_iter_start); 41 | % log 42 | logState; 43 | progBar; 44 | stop = stopCriteria; 45 | if stop 46 | break; 47 | end 48 | end 49 | % truncate log 50 | log.iter = log.iter(1:iter+1); 51 | log.t_iter = log.t_iter(1:iter+1); 52 | log.f_count = log.f_count(1:iter+1); 53 | log.f = log.f(1:iter+1); 54 | log.t = log.t(1:iter+1); 55 | log.X = log.X(:,:,1:iter+1); 56 | 57 | function logInit 58 | iter = 0; 59 | t_iter = 0; 60 | log.tag = obj.tag; 61 | log.iter = nan(1, max_iter+1); 62 | log.t_iter = nan(1, max_iter+1); 63 | log.f_count = nan(1, max_iter+1); 64 | log.f = nan(1, max_iter+1); 65 | log.t = nan(1, max_iter+1); 66 | log.X = nan([size(obj.X), max_iter+1]); 67 | log.exitflag = 'MaxIter'; 68 | end 69 | function logState 70 | log.iter(iter+1) = iter; 71 | log.t_iter(iter+1) = t_iter; 72 | log.f_count(iter+1) = obj.f_count; 73 | log.f(iter+1) = obj.optimProblem.evaluateValue(obj.x); % do not count, for logging only 74 | log.t(iter+1) = obj.t; 75 | log.X(:,:,iter+1) = obj.X; 76 | end 77 | function progBar 78 | if obj.verbose>=1 79 | progressbar(iter, max_iter, 40); 80 | end 81 | end 82 | function stop = stopCriteria 83 | stop = false; 84 | if iter>1 85 | % TolX 86 | if norm(log.X(:,:,iter)-log.X(:,:,iter+1),'fro')=obj.stopCntAccept 92 | obj.report(2,' - stopped after %d iterations (TolX)\n', iter); 93 | log.exitflag = 'TolX'; 94 | stop = true; 95 | end 96 | % TolFun 97 | if abs(log.f(iter)-log.f(iter+1))=obj.stopCntAccept 103 | obj.report(2,' - stopped after %d iterations (TolFun)\n', iter); 104 | log.exitflag = 'TolFun'; 105 | stop = true; 106 | end 107 | end 108 | end 109 | end 110 | 111 | end 112 | 113 | end -------------------------------------------------------------------------------- /meshAQP/code/mathHelpers/SparseLU.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | classdef SparseLU < handle 10 | properties 11 | LHS; 12 | L; 13 | U; 14 | p; 15 | q; 16 | end 17 | methods 18 | function obj = SparseLU(LHS) 19 | if ~issparse(LHS) 20 | error('Argument must be a sparse matrix') 21 | end 22 | obj.LHS = LHS; 23 | [obj.L,obj.U, obj.p, obj.q] = lu(LHS, 'vector'); 24 | end 25 | function x = solve(obj,RHS) 26 | x(obj.q,:) = obj.U\(obj.L\(RHS(obj.p,:))); 27 | end 28 | end 29 | 30 | end 31 | 32 | -------------------------------------------------------------------------------- /meshAQP/code/mathHelpers/colStack.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | function y = colStack(x) 10 | 11 | y = x(:); -------------------------------------------------------------------------------- /meshAQP/code/mathHelpers/getTensorTranspose.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | function T = getTensorTranspose(n,m) 10 | % Returns a matrix T such that vec(X')=T*vec(X) for any nxm matrix X. 11 | % 12 | % Example: 13 | % n = 3; 14 | % m = 4; 15 | % X = zeros(n,m); 16 | % X(:)=1:numel(X); 17 | % T = getTensorTranspose(n,m); 18 | % vec(X') 19 | % T*vec(X) 20 | 21 | [i,j] = meshgrid(1:n,1:m); 22 | T = sparse(sub2ind([m n],j,i),sub2ind([n m],i,j),1); -------------------------------------------------------------------------------- /meshAQP/code/mathHelpers/solveConstrainedLS.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | function x = solveConstrainedLS(C, d, A, b) 10 | % minimize ||Cx-d|| s.t. Ax=b 11 | 12 | n_vars = size(A,2); 13 | n_eq = size(A,1); 14 | x_lambda = [C'*C A'; A sparse(n_eq,n_eq)] \ [C'*d; b]; 15 | x = x_lambda(1:n_vars); -------------------------------------------------------------------------------- /meshAQP/code/mathHelpers/spdiag.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | function D = spdiag(d) 10 | 11 | n = length(d); 12 | D = sparse(1:n, 1:n, d, n, n); -------------------------------------------------------------------------------- /meshAQP/code/meshHelpers/boundaryFaces.m: -------------------------------------------------------------------------------- 1 | % Code implementing the paper "Injective and Bounded Mappings in 3D". 2 | % Disclaimer: The code is provided as-is and without any guarantees. Please contact the author to report any bugs. 3 | % Written by Noam Aigerman, http://www.wisdom.weizmann.ac.il/~noamaig/ 4 | 5 | function [F,inds] = boundaryFaces(T,keep) 6 | %compute the boundary faces F of the given tetrahedral mesh 7 | % keep is an optional boolean vector that forces to keep all faces of 8 | % any tet that its cooresponding entry in "keep" is non-zero 9 | %returns: 10 | %F: n x 3 array of all faces from T that are boundary or belong to a tet 11 | % that is marked as 1 in "keep". 12 | %inds(i)==k iff the the boundary face F(i,:) belongs to the tetrahedron 13 | %T(k,:) 14 | 15 | %alec jacobson's code with slight modification 16 | 17 | if isempty(T) || (nargin>1 && isempty(keep)); 18 | F=[]; 19 | inds=[]; 20 | return; 21 | end 22 | if nargin==1 23 | keep=zeros(size(T,1),1); 24 | end 25 | % BOUNDARY_FACES 26 | % F = boundary_faces(T) 27 | % Determine boundary faces of tetrahedra stored in T 28 | % 29 | % Input: 30 | % T tetrahedron index list, m by 4, where m is the number of tetrahedra 31 | % 32 | % Output: 33 | % F list of boundary faces, n by 3, where n is the number of boundary faces 34 | % 35 | 36 | % get all faces 37 | allF = [ ... 38 | T(:,1) T(:,2) T(:,3); ... 39 | T(:,1) T(:,3) T(:,4); ... 40 | T(:,1) T(:,4) T(:,2); ... 41 | T(:,2) T(:,4) T(:,3)]; 42 | keep=[keep;keep;keep;keep]~=0; 43 | % sort rows so that faces are reorder in ascending order of indices 44 | sortedF = sort(allF,2); 45 | % determine uniqueness of faces 46 | [u,~,n] = unique(sortedF,'rows'); 47 | % determine counts for each unique face 48 | counts = accumarray(n(:), 1); 49 | % extract faces that only occurred once 50 | sorted_exteriorF = u(counts == 1,:); 51 | % find in original faces so that ordering of indices is correct 52 | use=ismember(sortedF,sorted_exteriorF,'rows')|keep; 53 | F = allF(use,:); 54 | 55 | if nargout>1 56 | inds=1:size(T,1); 57 | inds=[inds inds inds inds]'; 58 | 59 | inds=inds(use); 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /meshAQP/code/meshHelpers/computeInjectiveStepSize.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | function tmax = computeInjectiveStepSize(F,x,p,tol) 10 | 11 | % init 12 | % tmax = inf; 13 | dim = size(F,2)-1; 14 | 15 | switch dim 16 | case 2 17 | xy = reshape(x,[],dim); 18 | pq = reshape(p,[],dim); 19 | x = xy(:,1); x = x(F); 20 | y = xy(:,2); y = y(F); 21 | p = pq(:,1); p = p(F); 22 | q = pq(:,2); q = q(F); 23 | 24 | a = p(:,1).*q(:,2) - p(:,2).*q(:,1) - p(:,1).*q(:,3) + p(:,3).*q(:,1) + p(:,2).*q(:,3) - p(:,3).*q(:,2); 25 | b = p(:,1).*y(:,2) - p(:,2).*y(:,1) - q(:,1).*x(:,2) + q(:,2).*x(:,1) - p(:,1).*y(:,3) + p(:,3).*y(:,1) + q(:,1).*x(:,3) - q(:,3).*x(:,1) + p(:,2).*y(:,3) - p(:,3).*y(:,2) - q(:,2).*x(:,3) + q(:,3).*x(:,2); 26 | c = x(:,1).*y(:,2) - x(:,2).*y(:,1) - x(:,1).*y(:,3) + x(:,3).*y(:,1) + x(:,2).*y(:,3) - x(:,3).*y(:,2); 27 | 28 | % regular cae 29 | ti1 = (-b+sqrt(b.^2-4*a.*c))/2./a; 30 | ti2 = (-b-sqrt(b.^2-4*a.*c))/2./a; 31 | % linear equation 32 | ff = (abs(a)<=tol); 33 | ti1(ff) = -c(ff)./b(ff); 34 | ti2(ff) = ti1(ff); 35 | % deal with pseudo complex roots 36 | ff=(abs(imag(ti1))<=tol); 37 | ti1(ff) = real(ti1(ff)); 38 | % 39 | ff=(abs(imag(ti2))<=tol); 40 | ti2(ff) = real(ti2(ff)); 41 | % get rid of negatives/imaginaries 42 | ti1(ti1<0 | imag(ti1)~=0) = inf; 43 | ti2(ti2<0 | imag(ti2)~=0) = inf; 44 | % take minimal value 45 | tmax = min(min(ti1),min(ti2)); 46 | 47 | case 3 48 | xyz = reshape(x,[],dim); 49 | pqr = reshape(p,[],dim); 50 | % 51 | x = xyz(:,1); x = x(F); 52 | y = xyz(:,2); y = y(F); 53 | z = xyz(:,3); z = z(F); 54 | % 55 | p = pqr(:,1); p = p(F); 56 | q = pqr(:,2); q = q(F); 57 | r = pqr(:,3); r = r(F); 58 | 59 | a = -p(:,1).*q(:,2).*r(:,3) + p(:,1).*r(:,2).*q(:,3) + q(:,1).*p(:,2).*r(:,3) - q(:,1).*r(:,2).*p(:,3) - r(:,1).*p(:,2).*q(:,3) + r(:,1).*q(:,2).*p(:,3) + p(:,1).*q(:,2).*r(:,4) - p(:,1).*r(:,2).*q(:,4) - q(:,1).*p(:,2).*r(:,4) + q(:,1).*r(:,2).*p(:,4) + r(:,1).*p(:,2).*q(:,4) - r(:,1).*q(:,2).*p(:,4) - p(:,1).*q(:,3).*r(:,4) + p(:,1).*r(:,3).*q(:,4) + q(:,1).*p(:,3).*r(:,4) - q(:,1).*r(:,3).*p(:,4) - r(:,1).*p(:,3).*q(:,4) + r(:,1).*q(:,3).*p(:,4) + p(:,2).*q(:,3).*r(:,4) - p(:,2).*r(:,3).*q(:,4) - q(:,2).*p(:,3).*r(:,4) + q(:,2).*r(:,3).*p(:,4) + r(:,2).*p(:,3).*q(:,4) - r(:,2).*q(:,3).*p(:,4); 60 | b = -x(:,1).*q(:,2).*r(:,3) + x(:,1).*r(:,2).*q(:,3) + y(:,1).*p(:,2).*r(:,3) - y(:,1).*r(:,2).*p(:,3) - z(:,1).*p(:,2).*q(:,3) + z(:,1).*q(:,2).*p(:,3) + x(:,2).*q(:,1).*r(:,3) - x(:,2).*r(:,1).*q(:,3) - y(:,2).*p(:,1).*r(:,3) + y(:,2).*r(:,1).*p(:,3) + z(:,2).*p(:,1).*q(:,3) - z(:,2).*q(:,1).*p(:,3) - x(:,3).*q(:,1).*r(:,2) + x(:,3).*r(:,1).*q(:,2) + y(:,3).*p(:,1).*r(:,2) - y(:,3).*r(:,1).*p(:,2) - z(:,3).*p(:,1).*q(:,2) + z(:,3).*q(:,1).*p(:,2) + x(:,1).*q(:,2).*r(:,4) - x(:,1).*r(:,2).*q(:,4) - y(:,1).*p(:,2).*r(:,4) + y(:,1).*r(:,2).*p(:,4) + z(:,1).*p(:,2).*q(:,4) - z(:,1).*q(:,2).*p(:,4) - x(:,2).*q(:,1).*r(:,4) + x(:,2).*r(:,1).*q(:,4) + y(:,2).*p(:,1).*r(:,4) - y(:,2).*r(:,1).*p(:,4) - z(:,2).*p(:,1).*q(:,4) + z(:,2).*q(:,1).*p(:,4) + x(:,4).*q(:,1).*r(:,2) - x(:,4).*r(:,1).*q(:,2) - y(:,4).*p(:,1).*r(:,2) + y(:,4).*r(:,1).*p(:,2) + z(:,4).*p(:,1).*q(:,2) - z(:,4).*q(:,1).*p(:,2) - x(:,1).*q(:,3).*r(:,4) + x(:,1).*r(:,3).*q(:,4) + y(:,1).*p(:,3).*r(:,4) - y(:,1).*r(:,3).*p(:,4) - z(:,1).*p(:,3).*q(:,4) + z(:,1).*q(:,3).*p(:,4) + x(:,3).*q(:,1).*r(:,4) - x(:,3).*r(:,1).*q(:,4) - y(:,3).*p(:,1).*r(:,4) + y(:,3).*r(:,1).*p(:,4) + z(:,3).*p(:,1).*q(:,4) - z(:,3).*q(:,1).*p(:,4) - x(:,4).*q(:,1).*r(:,3) + x(:,4).*r(:,1).*q(:,3) + y(:,4).*p(:,1).*r(:,3) - y(:,4).*r(:,1).*p(:,3) - z(:,4).*p(:,1).*q(:,3) + z(:,4).*q(:,1).*p(:,3) + x(:,2).*q(:,3).*r(:,4) - x(:,2).*r(:,3).*q(:,4) - y(:,2).*p(:,3).*r(:,4) + y(:,2).*r(:,3).*p(:,4) + z(:,2).*p(:,3).*q(:,4) - z(:,2).*q(:,3).*p(:,4) - x(:,3).*q(:,2).*r(:,4) + x(:,3).*r(:,2).*q(:,4) + y(:,3).*p(:,2).*r(:,4) - y(:,3).*r(:,2).*p(:,4) - z(:,3).*p(:,2).*q(:,4) + z(:,3).*q(:,2).*p(:,4) + x(:,4).*q(:,2).*r(:,3) - x(:,4).*r(:,2).*q(:,3) - y(:,4).*p(:,2).*r(:,3) + y(:,4).*r(:,2).*p(:,3) + z(:,4).*p(:,2).*q(:,3) - z(:,4).*q(:,2).*p(:,3); 61 | c = -x(:,1).*y(:,2).*r(:,3) + x(:,1).*z(:,2).*q(:,3) + x(:,1).*y(:,3).*r(:,2) - x(:,1).*z(:,3).*q(:,2) + y(:,1).*x(:,2).*r(:,3) - y(:,1).*z(:,2).*p(:,3) - y(:,1).*x(:,3).*r(:,2) + y(:,1).*z(:,3).*p(:,2) - z(:,1).*x(:,2).*q(:,3) + z(:,1).*y(:,2).*p(:,3) + z(:,1).*x(:,3).*q(:,2) - z(:,1).*y(:,3).*p(:,2) - x(:,2).*y(:,3).*r(:,1) + x(:,2).*z(:,3).*q(:,1) + y(:,2).*x(:,3).*r(:,1) - y(:,2).*z(:,3).*p(:,1) - z(:,2).*x(:,3).*q(:,1) + z(:,2).*y(:,3).*p(:,1) + x(:,1).*y(:,2).*r(:,4) - x(:,1).*z(:,2).*q(:,4) - x(:,1).*y(:,4).*r(:,2) + x(:,1).*z(:,4).*q(:,2) - y(:,1).*x(:,2).*r(:,4) + y(:,1).*z(:,2).*p(:,4) + y(:,1).*x(:,4).*r(:,2) - y(:,1).*z(:,4).*p(:,2) + z(:,1).*x(:,2).*q(:,4) - z(:,1).*y(:,2).*p(:,4) - z(:,1).*x(:,4).*q(:,2) + z(:,1).*y(:,4).*p(:,2) + x(:,2).*y(:,4).*r(:,1) - x(:,2).*z(:,4).*q(:,1) - y(:,2).*x(:,4).*r(:,1) + y(:,2).*z(:,4).*p(:,1) + z(:,2).*x(:,4).*q(:,1) - z(:,2).*y(:,4).*p(:,1) - x(:,1).*y(:,3).*r(:,4) + x(:,1).*z(:,3).*q(:,4) + x(:,1).*y(:,4).*r(:,3) - x(:,1).*z(:,4).*q(:,3) + y(:,1).*x(:,3).*r(:,4) - y(:,1).*z(:,3).*p(:,4) - y(:,1).*x(:,4).*r(:,3) + y(:,1).*z(:,4).*p(:,3) - z(:,1).*x(:,3).*q(:,4) + z(:,1).*y(:,3).*p(:,4) + z(:,1).*x(:,4).*q(:,3) - z(:,1).*y(:,4).*p(:,3) - x(:,3).*y(:,4).*r(:,1) + x(:,3).*z(:,4).*q(:,1) + y(:,3).*x(:,4).*r(:,1) - y(:,3).*z(:,4).*p(:,1) - z(:,3).*x(:,4).*q(:,1) + z(:,3).*y(:,4).*p(:,1) + x(:,2).*y(:,3).*r(:,4) - x(:,2).*z(:,3).*q(:,4) - x(:,2).*y(:,4).*r(:,3) + x(:,2).*z(:,4).*q(:,3) - y(:,2).*x(:,3).*r(:,4) + y(:,2).*z(:,3).*p(:,4) + y(:,2).*x(:,4).*r(:,3) - y(:,2).*z(:,4).*p(:,3) + z(:,2).*x(:,3).*q(:,4) - z(:,2).*y(:,3).*p(:,4) - z(:,2).*x(:,4).*q(:,3) + z(:,2).*y(:,4).*p(:,3) + x(:,3).*y(:,4).*r(:,2) - x(:,3).*z(:,4).*q(:,2) - y(:,3).*x(:,4).*r(:,2) + y(:,3).*z(:,4).*p(:,2) + z(:,3).*x(:,4).*q(:,2) - z(:,3).*y(:,4).*p(:,2); 62 | d = x(:,1).*z(:,2).*y(:,3) - x(:,1).*y(:,2).*z(:,3) + y(:,1).*x(:,2).*z(:,3) - y(:,1).*z(:,2).*x(:,3) - z(:,1).*x(:,2).*y(:,3) + z(:,1).*y(:,2).*x(:,3) + x(:,1).*y(:,2).*z(:,4) - x(:,1).*z(:,2).*y(:,4) - y(:,1).*x(:,2).*z(:,4) + y(:,1).*z(:,2).*x(:,4) + z(:,1).*x(:,2).*y(:,4) - z(:,1).*y(:,2).*x(:,4) - x(:,1).*y(:,3).*z(:,4) + x(:,1).*z(:,3).*y(:,4) + y(:,1).*x(:,3).*z(:,4) - y(:,1).*z(:,3).*x(:,4) - z(:,1).*x(:,3).*y(:,4) + z(:,1).*y(:,3).*x(:,4) + x(:,2).*y(:,3).*z(:,4) - x(:,2).*z(:,3).*y(:,4) - y(:,2).*x(:,3).*z(:,4) + y(:,2).*z(:,3).*x(:,4) + z(:,2).*x(:,3).*y(:,4) - z(:,2).*y(:,3).*x(:,4); 63 | 64 | %regular case - cubic equation 65 | delta0 = b.^2-3.*a.*c; 66 | delta1 = 2.*b.^3 - 9.*a.*b.*c + 27.*a.^2.*d; 67 | C=((delta1+sqrt(delta1.^2-4*delta0.^3))/2).^(1/3); 68 | u1 = 1; 69 | u2 = ((-1+sqrt(3)*sqrt(-1))/2); 70 | u3 = ((-1-sqrt(3)*sqrt(-1))/2); 71 | % 72 | ti1 = -(b+u1*C+delta0./(u1*C))./(3*a); 73 | ti2 = -(b+u2*C+delta0./(u2*C))./(3*a); 74 | ti3 = -(b+u3*C+delta0./(u3*C))./(3*a); 75 | % quadratic equation 76 | ff = (abs(a)<=tol); 77 | ti1(ff) = (-c(ff)+sqrt(c(ff).^2-4*b(ff).*d(ff)))/2./b(ff); 78 | ti2(ff) = (-c(ff)-sqrt(c(ff).^2-4*b(ff).*d(ff)))/2./b(ff); 79 | ti3(ff) = ti2(ff); 80 | % linear equation 81 | ff = (abs(a)<=tol & abs(b)<=tol); 82 | ti1(ff) = -d(ff)./c(ff); 83 | ti2(ff) = ti1(ff); 84 | ti3(ff) = ti1(ff); 85 | % deal with pseudo complex roots 86 | ff=(abs(imag(ti1))<=tol); 87 | ti1(ff) = real(ti1(ff)); 88 | % 89 | ff=(abs(imag(ti2))<=tol); 90 | ti2(ff) = real(ti2(ff)); 91 | % 92 | ff=(abs(imag(ti3))<=tol); 93 | ti3(ff) = real(ti3(ff)); 94 | % get rid of negatives/imaginaries 95 | ti1(ti1<0 | imag(ti1)~=0) = inf; 96 | ti2(ti2<0 | imag(ti2)~=0) = inf; 97 | ti3(ti3<0 | imag(ti3)~=0) = inf; 98 | % take minimal value 99 | tmax = min(min(min(ti1),min(ti2)),min(ti3)); 100 | 101 | otherwise 102 | error('invalid dimension') 103 | end -------------------------------------------------------------------------------- /meshAQP/code/meshHelpers/computeTutte.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | function [V0, inds_bF] = computeTutte(F,V,scale) 10 | 11 | % compute mesh gradient 12 | [T,areas] = computeMeshTranformationCoeffsMex(F, V); 13 | 14 | % constrain boundary to the circle 15 | TR = triangulation(F, V); 16 | temp = freeBoundary(TR); 17 | inds_bF = temp(:,1); %boundary vertices indices 18 | bn_vert = length(inds_bF); %number of boundary verts 19 | tet = 0: (2*pi / bn_vert) : 2*pi; 20 | tet(end)=[]; 21 | tet = tet'; 22 | [eq_lhs,eq_rhs] = indCoordsToLinearSystem(V(:,1:2), inds_bF, 1*[cos(tet) sin(tet)] ); 23 | 24 | % compute tutte 25 | wT = spdiag(kron(sqrt(areas), ones(4,1)))*T; 26 | V0 = reshape(solveConstrainedLS(wT,zeros(size(T,1),1),eq_lhs,eq_rhs),[],2); 27 | 28 | % global scale (isometric as possible) 29 | if scale 30 | V0 = ((T*V0(:))\colStack(repmat(eye(2),[1 1 size(F,1)]))) * V0; 31 | end 32 | 33 | -------------------------------------------------------------------------------- /meshAQP/code/meshHelpers/getDistortionColormap.m: -------------------------------------------------------------------------------- 1 | % Code implementing the paper "Injective and Bounded Mappings in 3D". 2 | % Disclaimer: The code is provided as-is and without any guarantees. Please contact the author to report any bugs. 3 | % Written by Noam Aigerman, http://www.wisdom.weizmann.ac.il/~noamaig/ 4 | 5 | function cols = getDistortionColormap() 6 | %set to out color map 7 | CBcols = [0.85 0.85 0.85];%[0.9 0.9 0.9]; 8 | t=(1:64) /64;t=t'; 9 | cols = (1-t)*CBcols + t*[0.7 0 0]; 10 | cols(cols>1) =1; 11 | caxis([1 10]); 12 | end 13 | 14 | -------------------------------------------------------------------------------- /meshAQP/code/meshHelpers/indCoordsToLinearSystem.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | function [eq_lhs,eq_rhs] = indCoordsToLinearSystem(X,ind,coords) 10 | 11 | eq_lhs_temp = sparse(1:length(ind), ind, 1, length(ind), size(X,1)); 12 | eq_lhs = kron(eq_lhs_temp,eye(size(X,2)))*getTensorTranspose(size(X,1),size(X,2)); 13 | eq_rhs = colStack(coords'); 14 | -------------------------------------------------------------------------------- /meshAQP/code/visualizeSolversForExamples.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | % Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | % Please contact the author to report any bugs. 5 | % Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | % Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | 10 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 11 | %%% This script visualizes the results of the examples provided in this 12 | %%% code package. 13 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 14 | 15 | %% init stuff 16 | solver_colors = {'b','m','c','r','k','g','y'}; 17 | switch size(F,2) 18 | case 3 19 | visF = F; 20 | case 4 21 | visF = boundaryFaces(F); 22 | end 23 | 24 | 25 | %% print summary 26 | for ii = 1:n_solvers 27 | fprintf('%s - %g sec\n', logs{ii}.tag, sum(logs{ii}.t_iter)); 28 | end 29 | 30 | 31 | %% visualize init state 32 | figure(100); clf; 33 | clear hP; 34 | for ii = 1:n_solvers 35 | hP{ii} = patch('vertices', optimProblem.x0, 'faces', visF, 'FaceColor', solver_colors{ii}, 'FaceAlpha', 0.4); 36 | end 37 | axis equal; 38 | legend(cellfun(@(x) x.tag, logs, 'UniformOutput', false)); 39 | if optimProblem.dim==3 40 | cameratoolbar; 41 | cameratoolbar('SetCoordSys','y'); 42 | end 43 | 44 | 45 | %% go through iterations 46 | figure(100); 47 | if visualizeIterations 48 | iterVec = 1:max(cellfun(@(x) length(x.iter), logs)); 49 | else 50 | iterVec = num_iter; 51 | end 52 | for iter = iterVec 53 | % update solvers 54 | for ii = 1:n_solvers 55 | try 56 | hP{ii}.Vertices = logs{ii}.X(:,:,min(iter, end)); 57 | catch 58 | warning('solver %d failed to draw', ii); 59 | end 60 | end 61 | title(iter); 62 | drawnow; 63 | pause(0.001); 64 | end 65 | 66 | 67 | %% plot some other information 68 | figure(101); clf; 69 | for ii = 1:n_solvers 70 | semilogy(logs{ii}.f, solver_colors{ii}); 71 | hold on; 72 | end 73 | ylabel('functional value'); 74 | xlabel('iteration #'); 75 | legend(cellfun(@(x) x.tag, logs, 'UniformOutput', false)); 76 | 77 | figure(102); clf; 78 | for ii = 1:n_solvers 79 | semilogy(logs{ii}.t, solver_colors{ii}); 80 | hold on; 81 | end 82 | ylabel('step size (t)'); 83 | xlabel('iteration #'); 84 | legend(cellfun(@(x) x.tag, logs, 'UniformOutput', false)); 85 | 86 | figure(103); clf; 87 | for ii = 1:n_solvers 88 | semilogy(cumsum([0; colStack(logs{ii}.t_iter(2:end))]), logs{ii}.f(1:end), solver_colors{ii}); 89 | hold on; 90 | end 91 | ylabel('functional value'); 92 | xlabel('time [sec]'); 93 | legend(cellfun(@(x) x.tag, logs, 'UniformOutput', false)); 94 | 95 | figure(104); clf; 96 | for ii = 1:n_solvers 97 | plot( abs(diff(logs{ii}.f))./(1+abs(logs{ii}.f(1:end-1))), solver_colors{ii}); 98 | hold on; 99 | end 100 | ylabel('relative functional error') 101 | xlabel('iteration #'); 102 | legend(cellfun(@(x) x.tag, logs, 'UniformOutput', false)); 103 | 104 | 105 | %% show per element energies (2d target domains only) 106 | if optimProblem.dim==2 107 | % compute per element energies 108 | clear e; 109 | for ii = 1:n_solvers 110 | try 111 | e{ii} = optimProblem.evaluatePerElementEnergy(solver{ii}.x); 112 | catch 113 | warning('solver %d failed to draw', ii); 114 | end 115 | end 116 | c_min = min(cellfun(@(x) min(x), e)); 117 | c_max = min(cellfun(@(x) max(x), e)); 118 | 119 | % compute boundary 120 | temp.TR = triangulation(optimProblem.F, optimProblem.x0); 121 | temp.indB = temp.TR.freeBoundary(); 122 | temp.indB = [temp.indB(:,1); temp.indB(1,1)]; 123 | if ~exist('boundary_line_width','var') 124 | boundary_line_width = 1; 125 | end 126 | 127 | for ii = 1:n_solvers 128 | try 129 | figure(400+ii); clf; 130 | patch('faces',optimProblem.F,'vertices',logs{ii}.X(:,:,end),'FaceVertexCData',e{ii},'facecolor','flat','EdgeAlpha',0.03); 131 | title(sprintf('%s\nmean energy: %g\nmedian energy: %g\nmax energy: %g',logs{ii}.tag, mean(e{ii}), median(e{ii}), max(e{ii}))); 132 | axis off; 133 | axis equal; 134 | colorbar; 135 | colormap(getDistortionColormap()); 136 | caxis([c_min c_max]); 137 | % boundary 138 | line(logs{ii}.X(temp.indB,1,end), logs{ii}.X(temp.indB,2,end),... 139 | 'color', solver_colors{ii}, 'linewidth', boundary_line_width); 140 | catch 141 | warning('solver %d failed to draw', ii); 142 | end 143 | end 144 | end -------------------------------------------------------------------------------- /meshAQP/mex/computeFunctionalIsoDistMex.cpp: -------------------------------------------------------------------------------- 1 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | //% Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | //% Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | //% Please contact the author to report any bugs. 5 | //% Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | //% Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | #include "mex.h" 10 | #include 11 | #include 12 | #include "mexHelpers.cpp" 13 | 14 | using namespace Eigen; 15 | 16 | void helperFcuntionalIsoDist2x2(VectorXd &pA, const VectorXd &areas, int dim, double& val, bool& flips) 17 | { 18 | int block_size = dim*dim; 19 | int num_blocks = pA.size() / block_size; 20 | Map currA(pA.data(), dim, dim); 21 | Matrix2d currA_inv; 22 | 23 | // project 24 | val = 0; 25 | flips = 0; 26 | for (int ii = 0; ii < num_blocks; ii++) 27 | { 28 | // get current block 29 | new (&currA) Map(pA.data() + ii*block_size, dim, dim); 30 | // check inverse 31 | flips = flips || (currA.determinant() < 0); 32 | // compute inverse 33 | currA_inv = currA.inverse(); 34 | val = val + areas(ii) * (currA.squaredNorm() + currA_inv.squaredNorm()); 35 | // compute Tx_grad 36 | currA = 2 * areas(ii) * (currA - currA_inv.transpose()*currA_inv*currA_inv.transpose()); 37 | } 38 | } 39 | 40 | void helperFcuntionalIsoDist3x3(VectorXd &pA, const VectorXd &areas, int dim, double& val, bool& flips) 41 | { 42 | int block_size = dim*dim; 43 | int num_blocks = pA.size() / block_size; 44 | Map currA(pA.data(), dim, dim); 45 | Matrix3d currA_inv; 46 | 47 | // project 48 | val = 0; 49 | flips = 0; 50 | for (int ii = 0; ii < num_blocks; ii++) 51 | { 52 | // get current block 53 | new (&currA) Map(pA.data() + ii*block_size, dim, dim); 54 | // check inverse 55 | flips = flips || (currA.determinant() < 0); 56 | // compute inverse 57 | currA_inv = currA.inverse(); 58 | val = val + areas(ii) * (currA.squaredNorm() + currA_inv.squaredNorm()); 59 | // compute Tx_grad 60 | currA = 2 * areas(ii) * (currA - currA_inv.transpose()*currA_inv*currA_inv.transpose()); 61 | } 62 | } 63 | 64 | void mexFunction(int nlhs, mxArray *plhs[], 65 | int nrhs, const mxArray*prhs[]) 66 | { 67 | // assign input 68 | int A_rows = mxGetM(prhs[0]); // # rows of A 69 | int A_cols = mxGetN(prhs[0]); // # cols of A 70 | int areas_rows = mxGetM(prhs[1]); // # rows of A 71 | int areas_cols = mxGetN(prhs[1]); // # cols of A 72 | double *dim; 73 | double val; 74 | bool flips; 75 | const Map A(mxGetPr(prhs[0]), A_rows, A_cols); 76 | const Map areas(mxGetPr(prhs[1]), areas_rows, areas_cols); 77 | dim = mxGetPr(prhs[2]); 78 | 79 | if (A_cols!=1) 80 | mexErrMsgIdAndTxt("MATLAB:wrong_input", "first argument must be a column vector"); 81 | if (areas_cols != 1) 82 | mexErrMsgIdAndTxt("MATLAB:wrong_input", "second argument must be a column vector"); 83 | 84 | // copy 85 | VectorXd pA(A_rows); 86 | pA = A; 87 | 88 | // compute 89 | if (*dim == 2) 90 | helperFcuntionalIsoDist2x2(pA, areas, *dim, val, flips); 91 | else if (*dim == 3) 92 | helperFcuntionalIsoDist3x3(pA, areas, *dim, val, flips); 93 | else 94 | mexErrMsgIdAndTxt("MATLAB:wrong_dimension", "dim must be either 2 or 3"); 95 | 96 | // output 97 | plhs[0] = mxCreateDoubleScalar(val); // functional value 98 | mapDenseMatrixToMex(pA, &(plhs[1])); // return Tx_grad 99 | plhs[2] = mxCreateLogicalScalar(flips); // were there any flips 100 | } -------------------------------------------------------------------------------- /meshAQP/mex/computeFunctionalIsoDistMex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/meshAQP/mex/computeFunctionalIsoDistMex.mexw64 -------------------------------------------------------------------------------- /meshAQP/mex/computeInjectiveStepSizeMex.cpp: -------------------------------------------------------------------------------- 1 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | //% Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | //% Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | //% Please contact the author to report any bugs. 5 | //% Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | //% Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | #include "mex.h" 10 | #include 11 | #include 12 | #include 13 | #include "mexHelpers.cpp" 14 | 15 | using namespace Eigen; 16 | using namespace std; 17 | 18 | double getSmallestPositiveRealQuadRoot(double a, double b, double c, double tol) 19 | { 20 | // return negative value if no positive real root is found 21 | double t; 22 | 23 | if (abs(a) <= tol) 24 | t = -c / b; 25 | else 26 | { 27 | double desc = b*b - 4 * a*c; 28 | if (desc > 0) 29 | { 30 | t = (-b - sqrt(desc)) / (2 * a); 31 | if (t < 0) 32 | t = (-b + sqrt(desc)) / (2 * a); 33 | } 34 | else // desv<0 ==> imag 35 | t = -1; 36 | } 37 | return t; 38 | } 39 | 40 | 41 | void computeInjectiveStepSize_2d(const MatrixXd& F, const MatrixXd& x, const MatrixXd& p, double tol, double& t_max) 42 | { 43 | int n_tri = F.rows(); 44 | double x1, x2, x3, y1, y2, y3; 45 | double p1, p2, p3, q1, q2, q3; 46 | double a, b, c, t; 47 | 48 | t_max = -1; 49 | for (int ii = 0; ii < n_tri; ii++) 50 | { 51 | x1 = x(F(ii, 0), 0); 52 | x2 = x(F(ii, 1), 0); 53 | x3 = x(F(ii, 2), 0); 54 | 55 | y1 = x(F(ii, 0), 1); 56 | y2 = x(F(ii, 1), 1); 57 | y3 = x(F(ii, 2), 1); 58 | 59 | p1 = p(F(ii, 0), 0); 60 | p2 = p(F(ii, 1), 0); 61 | p3 = p(F(ii, 2), 0); 62 | 63 | q1 = p(F(ii, 0), 1); 64 | q2 = p(F(ii, 1), 1); 65 | q3 = p(F(ii, 2), 1); 66 | 67 | a = p1*q2 - p2*q1 - p1*q3 + p3*q1 + p2*q3 - p3*q2; 68 | b = p1*y2 - p2*y1 - q1*x2 + q2*x1 - p1*y3 + p3*y1 + q1*x3 - q3*x1 + p2*y3 - p3*y2 - q2*x3 + q3*x2; 69 | c = x1*y2 - x2*y1 - x1*y3 + x3*y1 + x2*y3 - x3*y2; 70 | 71 | t = getSmallestPositiveRealQuadRoot(a, b, c, tol); 72 | if (t >= 0) 73 | if ((t_max < 0) | (t_max>t)) 74 | t_max = t; 75 | } 76 | } 77 | 78 | 79 | double getSmallestPositiveRealCubicRoot(double a, double b, double c, double d, double tol) 80 | { 81 | // return negative value if no positive real root is found 82 | double t = -1; 83 | 84 | if (abs(a) <= tol) 85 | t = getSmallestPositiveRealQuadRoot(b, c, d, tol); 86 | else 87 | { 88 | complex i(0, 1); 89 | complex delta0(b*b - 3 * a*c, 0); 90 | complex delta1(2 * b*b*b - 9 * a*b*c + 27 * a*a*d, 0); 91 | complex C = pow((delta1 + sqrt(delta1*delta1 - 4.0 * delta0*delta0*delta0)) / 2.0, 1.0 / 3.0); 92 | 93 | complex u2 = (-1.0 + sqrt(3.0)*i) / 2.0; 94 | complex u3 = (-1.0 - sqrt(3.0)*i) / 2.0; 95 | 96 | complex t1 = (b + C + delta0 / C) / (-3.0*a); 97 | complex t2 = (b + u2*C + delta0 / (u2*C)) / (-3.0*a); 98 | complex t3 = (b + u3*C + delta0 / (u3*C)) / (-3.0*a); 99 | 100 | if ((abs(imag(t1))0)) 101 | t = real(t1); 102 | if ((abs(imag(t2))0) && ((real(t2) < t) || (t < 0))) 103 | t = real(t2); 104 | if ((abs(imag(t3))0) && ((real(t3) < t) || (t < 0))) 105 | t = real(t3); 106 | } 107 | return t; 108 | } 109 | 110 | 111 | void computeInjectiveStepSize_3d(const MatrixXd& F, const MatrixXd& x, const MatrixXd& p, double tol, double& t_max) 112 | { 113 | int n_tri = F.rows(); 114 | double x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4; 115 | double p1, p2, p3, p4, q1, q2, q3, q4, r1, r2, r3, r4; 116 | double a, b, c, d, t; 117 | 118 | t_max = -1; 119 | for (int ii = 0; ii < n_tri; ii++) 120 | { 121 | x1 = x(F(ii, 0), 0); 122 | x2 = x(F(ii, 1), 0); 123 | x3 = x(F(ii, 2), 0); 124 | x4 = x(F(ii, 3), 0); 125 | 126 | y1 = x(F(ii, 0), 1); 127 | y2 = x(F(ii, 1), 1); 128 | y3 = x(F(ii, 2), 1); 129 | y4 = x(F(ii, 3), 1); 130 | 131 | z1 = x(F(ii, 0), 2); 132 | z2 = x(F(ii, 1), 2); 133 | z3 = x(F(ii, 2), 2); 134 | z4 = x(F(ii, 3), 2); 135 | 136 | p1 = p(F(ii, 0), 0); 137 | p2 = p(F(ii, 1), 0); 138 | p3 = p(F(ii, 2), 0); 139 | p4 = p(F(ii, 3), 0); 140 | 141 | q1 = p(F(ii, 0), 1); 142 | q2 = p(F(ii, 1), 1); 143 | q3 = p(F(ii, 2), 1); 144 | q4 = p(F(ii, 3), 1); 145 | 146 | r1 = p(F(ii, 0), 2); 147 | r2 = p(F(ii, 1), 2); 148 | r3 = p(F(ii, 2), 2); 149 | r4 = p(F(ii, 3), 2); 150 | 151 | a = -p1*q2*r3 + p1*r2*q3 + q1*p2*r3 - q1*r2*p3 - r1*p2*q3 + r1*q2*p3 + p1*q2*r4 - p1*r2*q4 - q1*p2*r4 + q1*r2*p4 + r1*p2*q4 - r1*q2*p4 - p1*q3*r4 + p1*r3*q4 + q1*p3*r4 - q1*r3*p4 - r1*p3*q4 + r1*q3*p4 + p2*q3*r4 - p2*r3*q4 - q2*p3*r4 + q2*r3*p4 + r2*p3*q4 - r2*q3*p4; 152 | b = -x1*q2*r3 + x1*r2*q3 + y1*p2*r3 - y1*r2*p3 - z1*p2*q3 + z1*q2*p3 + x2*q1*r3 - x2*r1*q3 - y2*p1*r3 + y2*r1*p3 + z2*p1*q3 - z2*q1*p3 - x3*q1*r2 + x3*r1*q2 + y3*p1*r2 - y3*r1*p2 - z3*p1*q2 + z3*q1*p2 + x1*q2*r4 - x1*r2*q4 - y1*p2*r4 + y1*r2*p4 + z1*p2*q4 - z1*q2*p4 - x2*q1*r4 + x2*r1*q4 + y2*p1*r4 - y2*r1*p4 - z2*p1*q4 + z2*q1*p4 + x4*q1*r2 - x4*r1*q2 - y4*p1*r2 + y4*r1*p2 + z4*p1*q2 - z4*q1*p2 - x1*q3*r4 + x1*r3*q4 + y1*p3*r4 - y1*r3*p4 - z1*p3*q4 + z1*q3*p4 + x3*q1*r4 - x3*r1*q4 - y3*p1*r4 + y3*r1*p4 + z3*p1*q4 - z3*q1*p4 - x4*q1*r3 + x4*r1*q3 + y4*p1*r3 - y4*r1*p3 - z4*p1*q3 + z4*q1*p3 + x2*q3*r4 - x2*r3*q4 - y2*p3*r4 + y2*r3*p4 + z2*p3*q4 - z2*q3*p4 - x3*q2*r4 + x3*r2*q4 + y3*p2*r4 - y3*r2*p4 - z3*p2*q4 + z3*q2*p4 + x4*q2*r3 - x4*r2*q3 - y4*p2*r3 + y4*r2*p3 + z4*p2*q3 - z4*q2*p3; 153 | c = -x1*y2*r3 + x1*z2*q3 + x1*y3*r2 - x1*z3*q2 + y1*x2*r3 - y1*z2*p3 - y1*x3*r2 + y1*z3*p2 - z1*x2*q3 + z1*y2*p3 + z1*x3*q2 - z1*y3*p2 - x2*y3*r1 + x2*z3*q1 + y2*x3*r1 - y2*z3*p1 - z2*x3*q1 + z2*y3*p1 + x1*y2*r4 - x1*z2*q4 - x1*y4*r2 + x1*z4*q2 - y1*x2*r4 + y1*z2*p4 + y1*x4*r2 - y1*z4*p2 + z1*x2*q4 - z1*y2*p4 - z1*x4*q2 + z1*y4*p2 + x2*y4*r1 - x2*z4*q1 - y2*x4*r1 + y2*z4*p1 + z2*x4*q1 - z2*y4*p1 - x1*y3*r4 + x1*z3*q4 + x1*y4*r3 - x1*z4*q3 + y1*x3*r4 - y1*z3*p4 - y1*x4*r3 + y1*z4*p3 - z1*x3*q4 + z1*y3*p4 + z1*x4*q3 - z1*y4*p3 - x3*y4*r1 + x3*z4*q1 + y3*x4*r1 - y3*z4*p1 - z3*x4*q1 + z3*y4*p1 + x2*y3*r4 - x2*z3*q4 - x2*y4*r3 + x2*z4*q3 - y2*x3*r4 + y2*z3*p4 + y2*x4*r3 - y2*z4*p3 + z2*x3*q4 - z2*y3*p4 - z2*x4*q3 + z2*y4*p3 + x3*y4*r2 - x3*z4*q2 - y3*x4*r2 + y3*z4*p2 + z3*x4*q2 - z3*y4*p2; 154 | d = x1*z2*y3 - x1*y2*z3 + y1*x2*z3 - y1*z2*x3 - z1*x2*y3 + z1*y2*x3 + x1*y2*z4 - x1*z2*y4 - y1*x2*z4 + y1*z2*x4 + z1*x2*y4 - z1*y2*x4 - x1*y3*z4 + x1*z3*y4 + y1*x3*z4 - y1*z3*x4 - z1*x3*y4 + z1*y3*x4 + x2*y3*z4 - x2*z3*y4 - y2*x3*z4 + y2*z3*x4 + z2*x3*y4 - z2*y3*x4; 155 | 156 | 157 | t = getSmallestPositiveRealCubicRoot(a, b, c, d, tol); 158 | if (t >= 0) 159 | if ((t_max < 0) | (t_max>t)) 160 | t_max = t; 161 | } 162 | } 163 | 164 | void mexFunction(int nlhs, mxArray *plhs[], 165 | int nrhs, const mxArray*prhs[]) 166 | { 167 | // assign input 168 | int n_tri = mxGetM(prhs[0]); // # rows of F 169 | int d_simplex = mxGetN(prhs[0]); // # cols of F 170 | int dim = d_simplex - 1; 171 | int n_vars = mxGetM(prhs[1]); // # rows of x/p 172 | int n_vert = n_vars / dim; 173 | const Map Fmatlab(mxGetPr(prhs[0]), n_tri, d_simplex); 174 | const Map x(mxGetPr(prhs[1]), n_vert, dim); 175 | const Map p(mxGetPr(prhs[2]), n_vert, dim); 176 | double *tol; 177 | tol = mxGetPr(prhs[3]); 178 | 179 | // update index numbers to 0-base 180 | MatrixXd F (Fmatlab); 181 | F = F.array() - 1; 182 | 183 | // compute 184 | double t_max; 185 | 186 | // compute 187 | if (dim == 2) 188 | computeInjectiveStepSize_2d(F, x, p, *tol, t_max); 189 | else if (dim == 3) 190 | computeInjectiveStepSize_3d(F, x, p, *tol, t_max); 191 | else 192 | mexErrMsgIdAndTxt("MATLAB:wrong_dimension", "dim must be either 2 or 3"); 193 | 194 | 195 | // assign outputs 196 | if (t_max < 0) 197 | t_max = mxGetInf(); 198 | plhs[0] = mxCreateDoubleScalar(t_max); // functional value 199 | 200 | } -------------------------------------------------------------------------------- /meshAQP/mex/computeInjectiveStepSizeMex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/meshAQP/mex/computeInjectiveStepSizeMex.mexw64 -------------------------------------------------------------------------------- /meshAQP/mex/computeMeshTranformationCoeffsMex.cpp: -------------------------------------------------------------------------------- 1 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | //% Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | //% Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | //% Please contact the author to report any bugs. 5 | //% Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | //% Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | #include "mex.h" 10 | #include 11 | #include 12 | #include "mexHelpers.cpp" 13 | 14 | using namespace Eigen; 15 | 16 | void computeMeshTranformationCoeffsFullDim(const MatrixXd& F, const MatrixXd& V, SparseMatrix &T, VectorXd& areas) 17 | { 18 | // init 19 | int n_tri = F.rows(); 20 | int d_simplex = F.cols(); 21 | int n_vert = V.rows(); 22 | int dim = V.cols(); 23 | int T_rows = n_tri*dim*dim; 24 | int T_cols = n_vert*dim; 25 | int T_nnz = n_tri*dim*dim*d_simplex; 26 | 27 | 28 | // prepare centering matrix 29 | MatrixXd B = MatrixXd::Identity(d_simplex, d_simplex); 30 | B = B.array() - (1.0 / d_simplex); 31 | 32 | // prepare output matrix 33 | T.resize(T_cols, T_rows); 34 | T.reserve(VectorXi::Constant(T_rows, d_simplex)); 35 | areas.resize(n_tri); 36 | 37 | // calculate differential coefficients for each element 38 | MatrixXd currV(d_simplex, dim); 39 | MatrixXd currT(dim, d_simplex); 40 | int curr_row = 0; 41 | for (int ii = 0; ii < n_tri; ii++) 42 | { 43 | // calculate current element 44 | for (int jj = 0; jj < d_simplex; jj++) 45 | { 46 | currV.row(jj) = V.row(F(ii, jj)); 47 | } 48 | currV = B*currV; // center 49 | currT = currV.fullPivLu().solve(B); // solver 50 | 51 | // fill into the correct places of T 52 | for (int cd = 0; cd < dim; cd++) 53 | for (int cr = 0; cr < dim; cr++) 54 | { 55 | for (int cc = 0; cc < d_simplex; cc++) 56 | T.insert(F(ii, cc) + (cd*n_vert), curr_row) = currT(cr, cc); 57 | curr_row += 1; 58 | } 59 | 60 | // calculate area 61 | areas(ii) = (currV.bottomRows(dim).rowwise() - currV.row(0)).determinant() / 2; 62 | } 63 | 64 | // compress 65 | .makeCompressed(); 66 | T = T.transpose(); 67 | } 68 | 69 | 70 | void orth(const MatrixXd &A, MatrixXd &Q) 71 | { 72 | 73 | //perform svd on A = U*S*V' (V is not computed and only the thin U is computed) 74 | Eigen::JacobiSVD svd(A, Eigen::ComputeThinU); 75 | Eigen::MatrixXd U = svd.matrixU(); 76 | const Eigen::VectorXd S = svd.singularValues(); 77 | 78 | //get rank of A 79 | int m = A.rows(); 80 | int n = A.cols(); 81 | double tol = std::max(m, n) * S.maxCoeff() * 2.2204e-16; 82 | int r = 0; 83 | for (int i = 0; i < S.rows(); ++r, ++i) 84 | { 85 | if (S[i] < tol) 86 | break; 87 | } 88 | 89 | //keep r first columns of U 90 | Q = U.block(0, 0, U.rows(), r); 91 | } 92 | 93 | void compute2dEmbedding(const MatrixXd& V, MatrixXd& A) 94 | { 95 | // given a nXn matrix whose columns are the vertices of a (n-1)-D simplex, 96 | // returns the transformation A, s.t A*V gives embedding in (n-1)-D 97 | 98 | MatrixXd ctrV(V.rows() - 1, V.cols()); 99 | ctrV = -V.bottomRows(V.rows() - 1); 100 | ctrV.rowwise() += V.row(0); 101 | ctrV.transpose(); 102 | orth(ctrV, A); 103 | //if (((ctrV*A).determinant()) < 0) 104 | // A.col(0).swap(A.col(1)); 105 | A.transpose(); 106 | } 107 | 108 | void embedTriangle(const MatrixXd& V, MatrixXd& flatV, double& area) 109 | { 110 | VectorXd v1 = V.row(1) - V.row(0); 111 | VectorXd v2 = V.row(2) - V.row(0); 112 | 113 | double norm_v1 = v1.norm(); 114 | double norm_v2 = v2.norm(); 115 | double cos_theta = v1.dot(v2) / (norm_v1*norm_v2); 116 | double sin_theta = sqrt(1 - cos_theta*cos_theta); 117 | 118 | flatV << 0, 0, 119 | norm_v1, 0, 120 | norm_v2*cos_theta, norm_v2*sin_theta; 121 | 122 | area = norm_v1*norm_v2*sin_theta / 2; 123 | } 124 | 125 | void computeMeshTranformationCoeffsFlatenning(const MatrixXd& F, const MatrixXd& V, SparseMatrix &T, VectorXd& areas) 126 | { 127 | // init 128 | int n_tri = F.rows(); 129 | int d_simplex = F.cols(); 130 | int n_vert = V.rows(); 131 | int dim = V.cols(); 132 | int d_diff = dim - 1; 133 | int T_rows = n_tri*d_diff*d_diff; 134 | int T_cols = n_vert*d_diff; 135 | int T_nnz = n_tri*d_diff*d_diff*d_simplex; 136 | 137 | assert(d_simplex == 3 && dim == 3); 138 | 139 | // prepare centering matrix 140 | MatrixXd B = MatrixXd::Identity(d_simplex, d_simplex); 141 | B = B.array() - (1.0 / d_simplex); 142 | 143 | // prepare output matrix 144 | T.resize(T_cols, T_rows); 145 | T.reserve(VectorXi::Constant(T_rows, d_simplex)); 146 | areas.resize(n_tri); 147 | 148 | // calculate differential coefficients for each element 149 | MatrixXd currV(d_simplex, dim); 150 | MatrixXd currT(dim, d_simplex); 151 | MatrixXd RFlat(dim, d_diff); 152 | MatrixXd currVFlat(d_simplex, d_diff); 153 | int curr_row = 0; 154 | for (int ii = 0; ii < n_tri; ii++) 155 | { 156 | // calculate current element 157 | for (int jj = 0; jj < d_simplex; jj++) 158 | { 159 | currV.row(jj) = V.row(F(ii, jj)); 160 | } 161 | // transform to plane 162 | embedTriangle(currV, currVFlat, areas(ii)); // this only works for triangles 163 | // compute 164 | currVFlat = B*currVFlat; // center 165 | currT = currVFlat.fullPivLu().solve(B); // solver 166 | 167 | // fill into the correct places of T 168 | for (int cd = 0; cd < d_diff; cd++) 169 | for (int cr = 0; cr < d_diff; cr++) 170 | { 171 | for (int cc = 0; cc < d_simplex; cc++) 172 | T.insert(F(ii, cc) + (cd*n_vert), curr_row) = currT(cr, cc); 173 | curr_row += 1; 174 | } 175 | } 176 | 177 | // compress 178 | T.makeCompressed(); 179 | T = T.transpose(); 180 | } 181 | 182 | void mexFunction(int nlhs, mxArray *plhs[], 183 | int nrhs, const mxArray*prhs[]) 184 | { 185 | // assign input 186 | int n_tri = mxGetM(prhs[0]); // # rows of F 187 | int d_simplex = mxGetN(prhs[0]); // # cols of F 188 | int n_vert = mxGetM(prhs[1]); // # rows of V 189 | int dim = mxGetN(prhs[1]); // # cols of V 190 | const Map Fmatlab(mxGetPr(prhs[0]), n_tri, d_simplex); 191 | const Map V(mxGetPr(prhs[1]), n_vert, dim); 192 | 193 | // update index numbers to 0-base 194 | MatrixXd F (Fmatlab); 195 | F = F.array() - 1; 196 | 197 | // compute 198 | SparseMatrix T; 199 | VectorXd areas; 200 | if (d_simplex == 3 && dim == 2) 201 | { 202 | // Planar triangulation 203 | computeMeshTranformationCoeffsFullDim(F, V, T, areas); 204 | } 205 | else if (d_simplex == 4 && dim == 3) 206 | { 207 | // Tet mesh 208 | computeMeshTranformationCoeffsFullDim(F, V, T, areas); 209 | } 210 | else if (d_simplex == 3 && dim == 3) 211 | { 212 | // 3D surface 213 | computeMeshTranformationCoeffsFlatenning(F, V, T, areas); 214 | } 215 | else 216 | mexErrMsgIdAndTxt("MATLAB:invalidInputs", "Invalid input dimensions or mesh type not supported"); 217 | 218 | 219 | // assign outputs 220 | mapSparseMatrixToMex(T, &(plhs[0])); 221 | mapDenseMatrixToMex(areas, &(plhs[1])); 222 | } -------------------------------------------------------------------------------- /meshAQP/mex/computeMeshTranformationCoeffsMex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/meshAQP/mex/computeMeshTranformationCoeffsMex.mexw64 -------------------------------------------------------------------------------- /meshAQP/mex/mexHelpers.cpp: -------------------------------------------------------------------------------- 1 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | //% Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | //% Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | //% Please contact the author to report any bugs. 5 | //% Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | //% Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | #include "mex.h" 10 | #include 11 | #include 12 | 13 | using namespace Eigen; 14 | 15 | void mapSparseMatrixToMex(const SparseMatrix& mat, mxArray **out) 16 | { 17 | *out = mxCreateSparse(mat.rows(), mat.cols(), mat.nonZeros(), mxREAL); 18 | 19 | mwIndex *Ir, *Jc; 20 | double *Pr; 21 | Ir = mxGetIr(*out); 22 | Jc = mxGetJc(*out); 23 | Pr = mxGetPr(*out); 24 | 25 | for (int k = 0; k < mat.nonZeros(); k++) 26 | { 27 | Pr[k] = (mat.valuePtr())[k]; 28 | Ir[k] = (mat.innerIndexPtr())[k]; 29 | } 30 | for (int k = 0; k <= mat.cols(); k++) 31 | { 32 | Jc[k] = (mat.outerIndexPtr())[k]; 33 | } 34 | } 35 | 36 | void mapDenseMatrixToMex(const MatrixXd& mat, mxArray **out) 37 | { 38 | *out = mxCreateDoubleMatrix(mat.rows(), mat.cols(), mxREAL); 39 | Map temp(mxGetPr(*out), mat.rows(), mat.cols()); 40 | temp = mat; // copy 41 | } -------------------------------------------------------------------------------- /meshAQP/mex/projectRotationMex.cpp: -------------------------------------------------------------------------------- 1 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | //% Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | //% Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | //% Please contact the author to report any bugs. 5 | //% Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | //% Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | #include "mex.h" 10 | #include 11 | #include 12 | #include "mexHelpers.cpp" 13 | 14 | using namespace Eigen; 15 | 16 | void projBlockRotation(VectorXd &pA, int dim) 17 | { 18 | int block_size = dim*dim; 19 | int num_blocks = pA.size() / block_size; 20 | JacobiSVD svdA(dim, dim, (ComputeFullU | ComputeFullV)); 21 | bool flipped; 22 | MatrixXd U, V; 23 | Map currA(pA.data(), dim, dim); 24 | 25 | // project 26 | for (int ii = 0; ii < num_blocks; ii++) 27 | { 28 | // get current block 29 | new (&currA) Map(pA.data() + ii*block_size, dim, dim); 30 | // sign of determinant 31 | flipped = (currA.determinant() < 0); 32 | // svd 33 | svdA.compute(currA); 34 | // compute frames 35 | U = svdA.matrixU(); 36 | V = svdA.matrixV(); 37 | // ssvd 38 | if (flipped) 39 | { 40 | U.col(dim - 1) = -U.col(dim - 1); 41 | } 42 | // project block 43 | currA = U*V.transpose(); 44 | } 45 | } 46 | 47 | void mexFunction(int nlhs, mxArray *plhs[], 48 | int nrhs, const mxArray*prhs[]) 49 | 50 | { 51 | // assign input 52 | int A_rows = mxGetM(prhs[0]); // # rows of A 53 | int A_cols = mxGetN(prhs[0]); // # cols of A 54 | double *dim; 55 | const Map A(mxGetPr(prhs[0]), A_rows, A_cols); 56 | dim = mxGetPr(prhs[1]); 57 | 58 | // init output 59 | VectorXd pA(A_rows); 60 | pA = A; 61 | 62 | // project 63 | projBlockRotation(pA, *dim); 64 | 65 | // assign outputs 66 | mapDenseMatrixToMex(pA, &(plhs[0])); 67 | 68 | } -------------------------------------------------------------------------------- /meshAQP/mex/projectRotationMex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/meshAQP/mex/projectRotationMex.mexw64 -------------------------------------------------------------------------------- /meshAQP/mex/projectRotationMexFast.cpp: -------------------------------------------------------------------------------- 1 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | //% Code implementing the paper "Accelerated Quadratic Proxy for Geometric Optimization", SIGGRAPH 2016. 3 | //% Disclaimer: The code is provided as-is for academic use only and without any guarantees. 4 | //% Please contact the author to report any bugs. 5 | //% Written by Shahar Kovalsky (http://www.wisdom.weizmann.ac.il/~shaharko/) 6 | //% Meirav Galun (http://www.wisdom.weizmann.ac.il/~/meirav/) 7 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | #include "mex.h" 10 | #include 11 | #include 12 | #include "mexHelpers.cpp" 13 | #include 14 | 15 | using namespace Eigen; 16 | using namespace igl; 17 | 18 | void projBlockRotation2x2(VectorXd &pA, int dim) 19 | { 20 | int block_size = dim*dim; 21 | int num_blocks = pA.size() / block_size; 22 | Map currA(pA.data(), dim, dim); 23 | Vector2d b; 24 | 25 | // project 26 | for (int ii = 0; ii < num_blocks; ii++) 27 | { 28 | // get current block 29 | new (&currA) Map(pA.data() + ii*block_size, dim, dim); 30 | // closest similarity 31 | b << 0.5*(currA(0, 0) + currA(1, 1)), 0.5*(currA(0, 1) - currA(1, 0)); // first row of B 32 | // closest rotation 33 | b = b / b.norm(); 34 | currA << b(0), b(1), -b(1), b(0); 35 | } 36 | } 37 | 38 | void projBlockRotation3x3(VectorXd &pA, int dim) 39 | { 40 | int block_size = dim*dim; 41 | int num_blocks = pA.size() / block_size; 42 | 43 | Matrix3f currAf, R; 44 | Matrix3f U, V; 45 | Vector3f s; 46 | Map currA(pA.data()); 47 | 48 | // project 49 | for (int ii = 0; ii < num_blocks; ii++) 50 | { 51 | // get current block 52 | new (&currA) Map(pA.data() + ii*block_size, dim, dim); 53 | // 54 | currAf = currA.cast(); // double -> single 55 | svd3x3(currAf, U, s, V); // svd 56 | R = U * V.transpose(); // polar 57 | currA = R.cast(); 58 | } 59 | } 60 | 61 | void mexFunction(int nlhs, mxArray *plhs[], 62 | int nrhs, const mxArray*prhs[]) 63 | 64 | { 65 | // assign input 66 | int A_rows = mxGetM(prhs[0]); // # rows of A 67 | int A_cols = mxGetN(prhs[0]); // # cols of A 68 | double *dim; 69 | const Map A(mxGetPr(prhs[0]), A_rows, A_cols); 70 | dim = mxGetPr(prhs[1]); 71 | 72 | // init output 73 | VectorXd pA(A_rows); 74 | pA = A; 75 | 76 | // project 77 | if (*dim == 2) 78 | projBlockRotation2x2(pA, *dim); 79 | else if (*dim == 3) 80 | projBlockRotation3x3(pA, *dim); 81 | else 82 | mexErrMsgIdAndTxt("MATLAB:wrong_dimension", "dim must be either 2 or 3"); 83 | 84 | // assign outputs 85 | mapDenseMatrixToMex(pA, &(plhs[0])); 86 | 87 | } -------------------------------------------------------------------------------- /meshAQP/mex/projectRotationMexFast.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renjiec/GLID/d2534c14b0dc487a494d6855c93e4658054d1e4f/meshAQP/mex/projectRotationMexFast.mexw64 -------------------------------------------------------------------------------- /meshAQP/toolbox/LargeScaleBD/SolverProjector.m: -------------------------------------------------------------------------------- 1 | classdef (Abstract) SolverProjector < handle 2 | 3 | properties 4 | % problem 5 | T; % lift operator 6 | W; % norm weights 7 | eqLHS; 8 | eqRHS; 9 | x0; 10 | % solver 11 | mode; 12 | usePreFactorization = true; 13 | nVars; 14 | nEq; 15 | x; 16 | Tx; 17 | pTx; 18 | tanNormal; 19 | tanLHS; 20 | tanRHS; 21 | preFactorization; 22 | TWW; % T'*W'*W 23 | TWWT; % T'*W'*W*T 24 | % temporaries 25 | % ? 26 | % log 27 | % ? 28 | % display / log 29 | verbose = 4; 30 | t_iter; 31 | t_projectD; 32 | t_projectLinear; 33 | t_factorization; 34 | % aux 35 | lambdaMultiTangent = 10; 36 | end 37 | 38 | properties (Dependent) 39 | y; 40 | end 41 | 42 | methods (Abstract) 43 | projectD_(obj) 44 | end 45 | 46 | methods 47 | function obj = SolverProjector 48 | % empty constructor 49 | end 50 | 51 | function value = get.y(obj) 52 | value = reshape(obj.x,size(obj.x0)); 53 | end 54 | 55 | function initSolver(obj) 56 | obj.nVars = numel(obj.x0); 57 | obj.nEq = size(obj.eqLHS,1); 58 | obj.x = colStack(obj.x0); 59 | obj.Tx = obj.T*obj.x; 60 | obj.projectD(); 61 | obj.tanNormal = zeros(obj.nVars,1); 62 | obj.tanLHS = zeros(obj.nVars,1); 63 | obj.tanRHS = 0; 64 | obj.updateProblem(); 65 | end 66 | 67 | function updateProblem(obj) 68 | t_start = tic; 69 | obj.TWW = obj.T'*obj.W'*obj.W; 70 | obj.TWWT = obj.TWW*obj.T; 71 | if (obj.usePreFactorization) 72 | obj.factorize(); 73 | end 74 | obj.t_factorization = toc(t_start); 75 | obj.report(2,'Prectorization took (%.3g secs)\n', obj.t_factorization); 76 | end 77 | 78 | function factorize(obj) 79 | % construct KKT matrix 80 | LHS = [obj.TWWT, obj.eqLHS'; obj.eqLHS, sparse(obj.nEq,obj.nEq)]; 81 | % factorize 82 | obj.preFactorization = SparseLU(LHS); 83 | end 84 | 85 | function projectD(obj) 86 | t_start = tic; 87 | obj.projectD_(); 88 | obj.t_projectD = toc(t_start); 89 | end 90 | 91 | function projectLinear(obj) 92 | t_start = tic; 93 | switch obj.mode 94 | case SolverProjectorModeEnum.AltProj 95 | temp_RHS = [obj.TWW*obj.pTx; obj.eqRHS]; 96 | if (obj.usePreFactorization) 97 | temp_x_lambda = obj.preFactorization.solve(temp_RHS); 98 | else 99 | temp_LHS = [obj.TWWT, obj.eqLHS'; obj.eqLHS, sparse(obj.nEq,obj.nEq)]; 100 | temp_x_lambda = temp_LHS\temp_RHS; 101 | end 102 | obj.x = temp_x_lambda(1:obj.nVars); 103 | 104 | case SolverProjectorModeEnum.Tangent 105 | if any(obj.tanNormal) % avoid solving linear system if projecting on D didn't do anything 106 | obj.tanLHS = obj.tanNormal'*obj.T; 107 | obj.tanRHS = obj.tanNormal'*obj.pTx; 108 | if (obj.usePreFactorization) 109 | temp_rhs = [obj.TWW*obj.pTx; obj.eqRHS]; 110 | temp_Au = [obj.tanLHS'; zeros(obj.nEq,1)]; 111 | % % compute lambda update 112 | % temp_inv_LHS_RHS = obj.preFactorization.solve(temp_rhs); 113 | % temp_inv_LHS_AuT = obj.preFactorization.solve(temp_Au); 114 | % temp_lambda_u = (temp_Au'*temp_inv_LHS_RHS - obj.tanRHS) / (temp_Au'*temp_inv_LHS_AuT); 115 | % % compute x_lambda 116 | % temp_x_lambda_RHS = temp_rhs - temp_lambda_u*temp_Au; 117 | % temp_x_lambda = obj.preFactorization.solve(temp_x_lambda_RHS); 118 | Fm_c = obj.preFactorization.solve(temp_rhs); 119 | Fm_n = obj.preFactorization.solve(temp_Au); 120 | temp_x_lambda = Fm_c - (temp_Au'*Fm_c - obj.tanRHS)/(temp_Au'*Fm_n)*Fm_n; 121 | else 122 | error('Not implemented yet') 123 | end 124 | else % if tanNormal=0 125 | temp_RHS = [obj.TWW*obj.pTx; obj.eqRHS]; 126 | if (obj.usePreFactorization) 127 | temp_x_lambda = obj.preFactorization.solve(temp_RHS); 128 | else 129 | temp_LHS = [obj.TWWT, obj.eqLHS'; obj.eqLHS, sparse(obj.nEq,obj.nEq)]; 130 | temp_x_lambda = temp_LHS\temp_RHS; 131 | end 132 | end 133 | obj.x = temp_x_lambda(1:obj.nVars); 134 | 135 | case SolverProjectorModeEnum.MultiTangent 136 | if isa(obj,'SolverProjectorBD') 137 | % split normals 138 | manyTanNormals = sparse(1:length(obj.tanNormal), kron(1:(length(obj.tanNormal)/(obj.dim^2)),ones(1,obj.dim^2)), obj.tanNormal); 139 | % remove inactive tangents 140 | manyTanNormals = manyTanNormals(:,any(manyTanNormals)); 141 | % normalize 142 | norms = sqrt(sum(manyTanNormals.^2)); 143 | invNorms = sparse(1:length(norms),1:length(norms),1./norms); 144 | manyTanNormals = manyTanNormals*invNorms; 145 | % tangents 146 | obj.tanLHS = manyTanNormals'*obj.T; 147 | obj.tanRHS = manyTanNormals'*obj.pTx; 148 | % solve 149 | obj.x = solveConstrainedLS([obj.W*obj.T; sqrt(obj.lambdaMultiTangent)*obj.tanLHS],... 150 | [obj.W*obj.pTx; sqrt(obj.lambdaMultiTangent)*obj.tanRHS], obj.eqLHS, obj.eqRHS); 151 | %fprintf('# active constraints for multitangents: %d\n',size(obj.tanLHS,1)); 152 | else 153 | error('Problem type not supported'); 154 | end 155 | 156 | otherwise 157 | error('invalid mode'); 158 | end 159 | obj.t_projectLinear = toc(t_start); 160 | end 161 | 162 | function iterate(obj) 163 | t_start = tic; 164 | % lift 165 | obj.Tx = obj.T*obj.x; 166 | % project onto D 167 | obj.projectD(); 168 | % compute normal (=error) 169 | obj.tanNormal = obj.Tx - obj.pTx; 170 | % project onto linear constraints 171 | obj.projectLinear(); 172 | obj.t_iter = toc(t_start); 173 | end 174 | 175 | function iterateN(obj,n) 176 | for ii = 1:n 177 | obj.iterate(); 178 | end 179 | end 180 | 181 | function solve(obj) 182 | error('Not implemented yet') 183 | end 184 | 185 | function report(obj,verbosity,varargin) 186 | if verbosity<=obj.verbose 187 | fprintf(varargin{:}); 188 | end 189 | end 190 | end 191 | end 192 | 193 | -------------------------------------------------------------------------------- /meshAQP/toolbox/LargeScaleBD/SolverProjectorBD.m: -------------------------------------------------------------------------------- 1 | classdef SolverProjectorBD < SolverProjector 2 | 3 | properties 4 | K; 5 | lb; 6 | ub; 7 | dim; 8 | distortions; 9 | flips; 10 | minsv; 11 | maxsv; 12 | end 13 | 14 | methods 15 | function obj = SolverProjectorBD(F, V, eqLHS, eqRHS, K, lb, ub, x0, mode) 16 | t_start = tic; 17 | obj.eqLHS = eqLHS; 18 | obj.eqRHS = eqRHS; 19 | obj.x0 = x0; 20 | obj.mode = mode; 21 | obj.K = K; 22 | obj.lb = lb; 23 | obj.ub = ub; 24 | obj.dim = size(F,2)-1; 25 | [obj.T,areas] = computeMeshTranformationCoeffsMex(F, V); 26 | weights = kron(sqrt(areas),ones(obj.dim^2,1)); 27 | obj.W = sparse(1:length(weights),1:length(weights),weights); 28 | %obj.W = 1; 29 | obj.initSolver(); 30 | obj.report(1,'SolverProjectorBD is ready (%.3g secs)\n', toc(t_start)); 31 | end 32 | 33 | function projectD_(obj) 34 | [obj.pTx, obj.distortions, obj.flips, obj.minsv, obj.maxsv] = projectBDMex(obj.Tx, obj.dim, obj.K, obj.lb, obj.ub); 35 | end 36 | 37 | function solve(obj, iter_max, tol_err, verbose) 38 | 39 | fprintf('-----------------------------------------------------------------\n'); 40 | fprintf('BD PROJECTION (K=%g):\n', obj.K); 41 | fprintf('-----------------------------------------------------------------\n'); 42 | fprintf('initial max dist %g, flips %d, infeasible %d\n', max(obj.distortions), nnz(obj.flips), nnz((obj.distortions>obj.K)|obj.flips)); 43 | fprintf('(min sv %g, max sv %d)\n', min(abs(obj.minsv)), max(obj.maxsv)); 44 | fprintf('-----------------------------------------------------------------\n'); 45 | for iter = 1:iter_max 46 | obj.iterate(); 47 | err = mse(obj.tanNormal); 48 | if verbose 49 | fprintf('iter %d -- err: %g dist: %g flips: %g time: %g sec (pD:%d%%, pL:%d%%)\n',iter,err,max(obj.distortions),nnz(obj.flips),obj.t_iter,round(100*obj.t_projectD/obj.t_iter), round(100*obj.t_projectLinear/obj.t_iter)); 50 | end 51 | if (err stopping...\n'); 53 | break; 54 | end 55 | end 56 | fprintf('-----------------------------------------------------------------\n'); 57 | fprintf('final max dist %g, flips %d, infeasible %d\n', max(obj.distortions), nnz(obj.flips), nnz((obj.distortions>obj.K)|obj.flips)); 58 | fprintf('-----------------------------------------------------------------\n'); 59 | 60 | 61 | end 62 | end 63 | 64 | end 65 | 66 | -------------------------------------------------------------------------------- /meshAQP/toolbox/LargeScaleBD/SolverProjectorModeEnum.m: -------------------------------------------------------------------------------- 1 | classdef SolverProjectorModeEnum sv2 87 | 88 | e2 = Sv(:,1).*e + Sv(:,2).*U2.*conj(e); 89 | 90 | w2 = real( e(:,[2 3 1]).*conj(e2(:,[3 1 2])) )./Areas; 91 | L2 = sparse( t(:,[2 3 1 3 1 2]), t(:,[3 1 2 2 3 1]), [w2 w2]/2, nv, nv ); 92 | L2 = spdiags(-sum(L2,2), 0, L2); 93 | 94 | %% global poisson 95 | b = accumarray( reshape(t,[],1), reshape(e2*1i.*Rots, [], 1) ); 96 | 97 | z2 = (L2+P2Plhs) \ (b+P2Prhs); 98 | 99 | g = (L2+P2Plhs)*z - (b+P2Prhs); 100 | 101 | %% orientation preservation 102 | ls_t = min( min( maxtForPositiveArea( z(t)*VE(:,1:2), z2(t)*VE(:,1:2) ) )*0.9, 1 ); 103 | 104 | %% line search energy decreasing 105 | fMyFun = @(t) fDeformEnergy( z2*t + z*(1-t) ); 106 | normdz = norm(z-z2); 107 | dgdotfz = dot( [real(g); imag(g)], [real(z2-z); imag(z2-z)] ); 108 | fQPEstim = @(t) en+ls_alpha*t*dgdotfz; 109 | 110 | e_new = fMyFun(ls_t); 111 | while ls_t*normdz>1e-12 && e_new > fQPEstim(ls_t) 112 | ls_t = ls_t*ls_beta; 113 | e_new = fMyFun(ls_t); 114 | end 115 | en = e_new; 116 | 117 | fprintf('it: %3d, t: %.3e, en: %.3e\n', it, ls_t, en); 118 | 119 | %% update 120 | z = z2*ls_t + z*(1-ls_t); 121 | 122 | %% stats 123 | allStats(it+1, [5 7 8]) = [toc*1000 norm(z(P2PVtxIds)-P2PCurrentPositions)^2 en]; 124 | end 125 | 126 | allStats(:,7:8) = allStats(:,7:8)/sum(Areas); 127 | 128 | 129 | fprintf('%dits: mean runtime: %.3e\n', nIter, mean(allStats(2:end,5))); 130 | 131 | -------------------------------------------------------------------------------- /nlo_p2p_harmonic.m: -------------------------------------------------------------------------------- 1 | function [phipsyIters, allStats] = nlo_p2p_harmonic(D2, C2, bP2P, lambda, phipsyIters, energy_parameter, nIter, solver, energy_type, ... 2 | nextSampleInSameCage, hessianSampleRate, fillDistanceSegments, v, E2, L) 3 | 4 | cageSizes = gather( int32( cellfun(@numel, v) ) ); 5 | vv = cat(1, v{:}); 6 | 7 | n = size(D2,2); 8 | numEnergySamples = size(D2, 1); 9 | 10 | enEvalsPerKernel = 10; 11 | 12 | hessSampleStride = ceil(1/hessianSampleRate); 13 | hessian_samples = 1: hessSampleStride :ceil(numEnergySamples-hessSampleStride/2); 14 | if hasGPUComputing, hessian_samples = gpuArray(hessian_samples); end 15 | hessianSampleRate = numel(hessian_samples)/numEnergySamples; 16 | 17 | cageOffsets = cumsum([0 cageSizes(1) cageSizes(2:end)-2]); % remove first 2 vertex in each hole 18 | 19 | c_iso_energy_names = {'SymmDirichlet', 'Exp_SymmDirichlet', 'AMIPS' }; 20 | isometric_energy_type = find(strcmpi(c_iso_energy_names, energy_type), 1) - 1; 21 | if isempty(isometric_energy_type), isometric_energy_type = -1; end 22 | 23 | if numel(v)==1, v = v{1}; end 24 | 25 | optimization_methods = {'GD', 'Newton', 'Newton_SPDH', 'Newton_SPDH_FullEig'}; 26 | if strcmpi(solver(1:2), 'cu') %cuda solvers 27 | optmethod = find(strcmpi(solver(3:end), optimization_methods), 1) - 1; 28 | 29 | params = struct('hessian_samples', int32(hessian_samples-1), 'isometric_energy_type', isometric_energy_type, 'isometric_energy_power', energy_parameter, 'nIter', nIter, ... 30 | 'sample_spacings_half', fillDistanceSegments, 'v', vv, 'E2', E2, 'L', L, 'nextSampleInSameCage', int32(nextSampleInSameCage-1), ... 31 | 'LS_energy_eval_per_kernel', enEvalsPerKernel, 'solver', optmethod, 'linearSolvePref', 0, 'deltaFixSPDH', 1e-15, 'reportIterationStats', 1); 32 | % linearSolvePref PREFER_CHOLESKY = 0, PREFER_LU = 1, FORCE_CHOLESKY = 2 33 | % deltaFixSPDH, is relative to p2p_weight 34 | 35 | [phipsyIters, allStats] = cuHarmonic(D2, C2, bP2P, lambda, double(phipsyIters), cageOffsets, params); 36 | 37 | if size(allStats,1)>2 38 | allStats = allStats'; 39 | else 40 | allStats = [zeros(1, 7) allStats]; 41 | end 42 | return; 43 | end 44 | 45 | mu = energy_parameter; 46 | 47 | fDistortionGradImp = @(fzalpha1, gzalpha2, evec) 2*[D2'*(fzalpha1.*evec); conj(D2'*(gzalpha2.*evec))]; 48 | 49 | %% energy functions for different energy types 50 | switch energy_type 51 | case 'SymmDirichlet' 52 | if mu==1 53 | fIsometryicEnergyFzGz2 = @(fzgz2) sum( sum(fzgz2, 2).*(1+diff(fzgz2, 1, 2).^-2) ); 54 | fGradIso = @(fzgz, fzgz2) 2*[D2'*(fzgz(:,1).*(1-((fzgz2*[1;-1]).^-3).*(fzgz2*[1;3])) ); conj(D2'*(fzgz(:,2).*(1+((fzgz2*[1;-1]).^-3).*(fzgz2*[3;1]))))]; 55 | else 56 | fIsometryicEnergyFzGz2 = @(fzgz2) sum( abs(sum(fzgz2, 2).*(1+diff(fzgz2, 1, 2).^-2) ).^mu ); 57 | fGradIso = @(fzgz, fzgz2) fDistortionGradImp( fzgz(:,1).*(1+(diff(fzgz2,1,2).^-3).*(fzgz2*[1;3])), ... 58 | fzgz(:,2).*(1-(diff(fzgz2,1,2).^-3).*(fzgz2*[3;1])), ... 59 | mu*abs(sum(fzgz2,2).*(1+diff(fzgz2,1,2).^-2) ).^(mu-1) ); 60 | end 61 | case 'Exp_SymmDirichlet' 62 | fIsometryicEnergyFzGz2 = @(fzgz2) sum( exp( mu*abs(sum(fzgz2, 2).*(1+diff(fzgz2, 1, 2).^-2)) ) ); 63 | fGradIso = @(fzgz, fzgz2) fDistortionGradImp( fzgz(:,1).*(1+(diff(fzgz2,1,2).^-3).*(fzgz2*[1;3])), ... 64 | fzgz(:,2).*(1-(diff(fzgz2,1,2).^-3).*(fzgz2*[3;1])), ... 65 | mu*exp( mu*abs(sum(fzgz2,2).*(1+diff(fzgz2,1,2).^-2)) ) ); 66 | case 'AMIPS' 67 | fIsometryicEnergyFzGz2 = @(fzgz2) sum( exp( (mu*2*sum(fzgz2,2)+1)./-diff(fzgz2,1,2) - diff(fzgz2,1,2) ) ); 68 | fGradIso = @(fzgz, fzgz2) fDistortionGradImp( fzgz(:,1).* (1-(4*mu*fzgz2(:,2)+1).*diff(fzgz2,1,2).^-2), ... 69 | fzgz(:,2).*-(1-(4*mu*fzgz2(:,1)+1).*diff(fzgz2,1,2).^-2), ... 70 | exp( (mu*2*sum(fzgz2,2)+1)./-diff(fzgz2,1,2) - diff(fzgz2,1,2) ) ); 71 | otherwise 72 | assert(false); 73 | end 74 | 75 | fP2PEnergyPhiPsy = @(fg) norm(fg(:,1)+conj(fg(:,2)) - bP2P)^2; 76 | 77 | matGradP2P = 2*[C2 conj(C2)]'*[C2 conj(C2) -bP2P]; 78 | fGradP2P = @(phi, psy) matGradP2P*[phi; conj(psy); ones(1,size(phi,2))]; 79 | 80 | 81 | fC2Rm = @(x) [real(x) -imag(x); imag(x) real(x)]; 82 | fR2Cv = @(x) complex(x(1:end/2), x(end/2+1:end)); 83 | fC2Rv = @(x) [real(x); imag(x)]; 84 | 85 | 86 | CtC = [C2 conj(C2)]'*[C2 conj(C2)]; 87 | CtCr = fC2Rm(CtC); 88 | CtCr(1:n*3, n*3+(1:n)) = -CtCr(1:n*3, n*3+(1:n)); 89 | CtCr(n*3+(1:n), 1:n*3) = -CtCr(n*3+(1:n), 1:n*3); 90 | 91 | 92 | ls_beta = 0.5; 93 | ls_alpha = 0.2; 94 | 95 | fMyEnergy = @(fzgz2, fg) fIsometryicEnergyFzGz2(fzgz2) + fP2PEnergyPhiPsy(fg)*lambda; 96 | fMyGrad = @(fzgz, fzgz2, phi, psy) fGradIso(fzgz, fzgz2) + fGradP2P(phi, psy)*lambda; 97 | 98 | %% nullspace, reduce problem size by removing redundant vars 99 | if ~iscell(v) 100 | N = speye(2*n, 2*n-1); 101 | else 102 | cageSz = numel(v{1}); 103 | isFreeVar = true(1, n*2); 104 | nHoles = numel(v)-1; 105 | isFreeVar([n+cageSz 2*n+(1-nHoles:0)]) = false; 106 | N = sparse([find(isFreeVar) 2*n+(1-nHoles:0)], [1:sum(isFreeVar) n+(1-nHoles:0)], 1, 2*n, sum(isFreeVar)); 107 | end 108 | 109 | if hasGPUComputing && verLessThan('matlab', '9.2'), N = full(N); end 110 | 111 | 112 | % for real variables 113 | Nr = blkdiag(N, N); 114 | Nr(end-numel(v)+2:end,:) = -Nr(end-numel(v)+2:end,:); 115 | 116 | switch solver 117 | case 'LBFGS' 118 | %% identity initial hessian for isometric energy 119 | h = eye(2*n)*2; 120 | M = h + 2*lambda*CtC; 121 | 122 | invM = N*inv(N'*M*N)*N'; 123 | 124 | LBFGS_K = 5; % parameter k 125 | 126 | fzgzAll = D2*phipsyIters; 127 | gIterAll = reshape(phipsyIters, [], size(phipsyIters,2)/2)*0; 128 | for i=1:size(phipsyIters,2)/2 129 | j = i*2+(-1:0); 130 | gIterAll(:, i) = fGradIso(fzgzAll(:,j), abs(fzgzAll(:,j)).^2) + fGradP2P(phipsyIters(:, j(1)), phipsyIters(:, j(2)))*lambda; 131 | end 132 | 133 | 134 | allStats = zeros(nIter+1, 8); 135 | allStats(1, 8) = gather( fMyEnergy( abs(fzgzAll(:,1:2)).^2, C2*phipsyIters(:,1:2)) ); 136 | for it=1:nIter 137 | tic 138 | 139 | %% iteration 140 | fzgz = fzgzAll(:, 1:2); 141 | ppIter = reshape(phipsyIters, n*2, []); 142 | ppIter(n+1:end,:) = conj( ppIter(n+1:end,:) ); 143 | fgP2P = C2*phipsyIters(:, 1:2); 144 | 145 | dpp = lbfgs_iter(invM, gIterAll, ppIter); 146 | dppdotg = dot( [real(dpp); imag(dpp)], [real(gIterAll(:,1)); imag(gIterAll(:,1))] ); 147 | dpp = reshape(dpp, [], 2); 148 | 149 | normdpp = norm(dpp); 150 | dpp(:,2) = conj(dpp(:,2)); 151 | 152 | dfzgz = D2*dpp; 153 | dfgP2P = C2*dpp; 154 | fMyFun = @(t) fMyEnergy(abs(fzgz+t*dfzgz).^2, fgP2P+t*dfgP2P); 155 | 156 | maxts = arrayfun(@maxtForPhiPsy, fzgz(:,1), fzgz(:,2), dfzgz(:,1), dfzgz(:,2)); 157 | ls_t = min(1, min(maxts)*0.8); % faster than min( [maxts;1] ) 158 | 159 | e = fMyEnergy(abs(fzgz).^2, fgP2P); 160 | fQPEstim = @(t) e+ls_alpha*t*dppdotg; 161 | e_new = fMyFun(ls_t); 162 | while ls_t*normdpp>1e-12 && e_new > fQPEstim(ls_t) 163 | ls_t = ls_t/2; 164 | e_new = fMyFun(ls_t); 165 | end 166 | 167 | ls_t = lineSearchLocallyInjectiveHarmonicMap(phipsyIters(:,1:2), dpp, fzgz(:,1:2), dfzgz(:,1:2), ls_t, fillDistanceSegments, v, E2, L, nextSampleInSameCage); 168 | e_new = fMyFun(ls_t); 169 | 170 | phipsyIters = [phipsyIters(:,1:2)+ls_t*dpp phipsyIters(:,1:min(end,2*(LBFGS_K-1)))]; 171 | 172 | %% update for next iteration 173 | fzgz = D2*phipsyIters(:,1:2); 174 | 175 | gIter = fGradIso(fzgz, abs(fzgz).^2) + fGradP2P(phipsyIters(:, 1), phipsyIters(:, 2))*lambda; 176 | gIterAll = [gIter gIterAll(:, 1:min(end, (LBFGS_K-1)))]; 177 | fzgzAll = [fzgz fzgzAll(:, 1:min(end, 2*(LBFGS_K-1)))]; 178 | 179 | allStats(it+1, [5 8]) = [toc*1000 gather( real(e_new))]; 180 | end 181 | 182 | fprintf('LBFGS %diterations: mean runtime: %.3ems\n', it, mean(allStats(2:end,5))); 183 | 184 | case {'Newton', 'Newton_SPDH', 'Newton_ConformalMap', 'Newton_SPDH_FullEig', 'Gradient Descent'} 185 | phipsyIters = double(phipsyIters); 186 | 187 | phi = phipsyIters(:, 1); 188 | psy = phipsyIters(:, 2); 189 | 190 | fzgz0 = D2*[phi psy]; % gz: conj(fzb) 191 | fzgz2 = abs(fzgz0).^2; % abs2([fz conj(fzb)]) 192 | 193 | e = fMyEnergy(fzgz2, C2*[phi psy]); 194 | 195 | % statistics to be in consistant with that returned from cuda solvers 196 | allStats = zeros(nIter+1, 8); 197 | allStats(1, [7 8]) = gather( [fP2PEnergyPhiPsy(C2*[phi psy])*lambda e] ); 198 | 199 | for it=1:nIter 200 | tic; 201 | fgP2P = C2*[phi psy]; 202 | 203 | g = fMyGrad(fzgz0, fzgz2, phi, psy); 204 | 205 | if strncmpi(solver, 'Newton', 6) 206 | SPDHessian = strcmp(solver, 'Newton_SPDH'); 207 | [~, ~, h] = harmonicMapIsometryicEnergy(D2(hessian_samples,:), phi, psy, SPDHessian, energy_type, mu); 208 | 209 | h = h/hessianSampleRate; 210 | 211 | if strcmpi(solver, 'Newton_SPDH_FullEig') 212 | [eigD, eigE] = eig( tril(h) + tril(h)' - diag(diag(h)) ); 213 | eigE(eigE<0) = 0; 214 | h = eigD*eigE*eigD'; 215 | end 216 | 217 | M = h + 2*lambda*CtCr; 218 | end 219 | 220 | 221 | %% solve 222 | if strcmp(solver, 'Gradient Descent') 223 | dpp = -g; 224 | elseif strncmpi(solver, 'Newton', 6) 225 | g(n+1:n*2) = conj( g(n+1:n*2) ); 226 | dpp = fR2Cv( Nr*( (Nr'*M*Nr)\(Nr'*fC2Rv(-g)) ) ); 227 | end 228 | 229 | dppdotg = dot( [real(dpp); imag(dpp)], [real(g); imag(g)] ); 230 | normdpp = norm(dpp); 231 | 232 | if ~strncmpi(solver, 'Newton', 6) % no conjugate is needed for newton because of using real numbers 233 | dpp = [dpp(1:n); conj( dpp(n+1:2*n) )]; 234 | end 235 | 236 | dfzgz = D2*reshape(dpp, [], 2); 237 | dfgP2P = C2*reshape(dpp, [], 2); 238 | 239 | %% 240 | maxts = arrayfun(@maxtForPhiPsy, fzgz0(:,1), fzgz0(:,2), dfzgz(:,1), dfzgz(:,2)); 241 | ls_t = min(1, min(maxts)*0.8); % faster than min( [maxts;1] ) 242 | 243 | fQPEstim = @(t) e+ls_alpha*t*dppdotg; 244 | fMyFun = @(t) fMyEnergy(abs(fzgz0+t*dfzgz).^2, fgP2P+t*dfgP2P); 245 | 246 | e_new = fMyFun(ls_t); 247 | while ls_t*normdpp>1e-12 && e_new > fQPEstim(ls_t) 248 | ls_t = ls_t*ls_beta; 249 | e_new = fMyFun(ls_t); 250 | end 251 | e = e_new; 252 | 253 | dpp = reshape(dpp,[],2); 254 | 255 | ls_t = lineSearchLocallyInjectiveHarmonicMap(phipsyIters(:,1:2), dpp, fzgz0, dfzgz, ls_t, fillDistanceSegments, v, E2, L, nextSampleInSameCage); 256 | e = fMyFun(ls_t); 257 | 258 | allStats(it+1, [5 7 8]) = gather( [toc*1000 fP2PEnergyPhiPsy(fgP2P+ls_t*dfgP2P)*lambda e] ); 259 | 260 | if ls_t*normdpp<1e-12, break; end 261 | 262 | phi = phi + ls_t*dpp(:,1); 263 | psy = psy + ls_t*dpp(:,2); 264 | 265 | fzgz0 = fzgz0 + ls_t*dfzgz; 266 | fzgz2 = abs(fzgz0).^2; 267 | 268 | phipsyIters = [phi psy phipsyIters(:,1:end-2)]; 269 | end 270 | 271 | otherwise 272 | warning('Unexpected solver type: %s. ', solver); 273 | end 274 | -------------------------------------------------------------------------------- /p2p_harmonic.m: -------------------------------------------------------------------------------- 1 | fprintf('\n\n'); 2 | 3 | if hasGPUComputing 4 | myGPUArray = @(x) gpuArray(x); 5 | else 6 | myGPUArray = @(x) x; 7 | end 8 | 9 | %preprocessing - done once 10 | if needsPreprocessing 11 | v = cellfun(myGPUArray, v, 'UniformOutput', false); 12 | 13 | if ~exist('Phi', 'var') || numel(Phi) ~= numel(vv) 14 | Phi = gather(vv); 15 | Psy = Phi*0; 16 | end 17 | 18 | energySamples = myGPUArray(energySamples); 19 | 20 | %% preprocess for Modulus of Continuity 21 | [~, SoDerivativeOfCauchyCoordinatesAtEnergySamples] = derivativesOfCauchyCoord(v, energySamples, holeCenters); 22 | 23 | catv = myGPUArray( cat(1, v{:}) ); 24 | L2 = myGPUArray(zeros(numel(energySamples), numel(catv)+numel(holeCenters))); 25 | for i=1:numel(catv) 26 | L2(:, i) = distancePointToSegment(catv(i), energySamples, energySamples(nextSampleInSameCage)).^-2/2/pi; 27 | end 28 | 29 | %% Lipschitz for log basis in multiconeccted case 30 | for i=1:numel(holeCenters) 31 | L2(:, end-numel(holeCenters)+i) = distancePointToSegment(holeCenters(i), energySamples, energySamples(nextSampleInSameCage)).^-3*2; 32 | end 33 | 34 | DerivativeOfCauchyCoordinatesAtEnergySamples = derivativesOfCauchyCoord(v, energySamples, holeCenters); %copmute on gpu and transfer to cpu 35 | fillDistanceSegments = myGPUArray( abs(energySamples-energySamples(nextSampleInSameCage))/2 ); 36 | 37 | nextSampleInSameCage = myGPUArray(nextSampleInSameCage); 38 | 39 | needsPreprocessing = false; 40 | 41 | phi = Phi; psy = Psy; 42 | end 43 | 44 | total_time = tic; 45 | 46 | optimizationTimeOnly = tic; 47 | 48 | numVirtualVertices = size(CauchyCoordinatesAtP2Phandles, 2); 49 | numEnergySamples = size(DerivativeOfCauchyCoordinatesAtEnergySamples, 1); 50 | 51 | P2P_Deformation_Converged = 0; 52 | 53 | switch solver_type 54 | case 'AQP' 55 | [XP2PDeform, statsAll] = meshAQP(X, T, P2PVtxIds, P2PCurrentPositions, XP2PDeform, numIterations); 56 | 57 | case 'SLIM' 58 | [XP2PDeform, statsAll] = meshSLIM(X, T, P2PVtxIds, P2PCurrentPositions, XP2PDeform, numIterations, p2p_weight, energy_type, energy_parameter); 59 | 60 | otherwise 61 | %{'Newton', 'Newton SPDH'} 62 | if ~exist('NLO_preprocessed','var') || ~NLO_preprocessed 63 | D2 = myGPUArray( DerivativeOfCauchyCoordinatesAtEnergySamples ); 64 | 65 | NLO_preprocessed = true; 66 | nPhiPsyIters = 2; 67 | 68 | phipsyIters = repmat( myGPUArray( [phi psy] ), 1, nPhiPsyIters ); 69 | end 70 | 71 | C2 = myGPUArray(CauchyCoordinatesAtP2Phandles); 72 | 73 | if ~isempty(P2PCurrentPositions) 74 | [phipsyIters, statsAll] = nlo_p2p_harmonic(D2, C2, myGPUArray(P2PCurrentPositions), p2p_weight, ... 75 | phipsyIters, energy_parameter, numIterations, solver_type, energy_type, nextSampleInSameCage, ... 76 | hessianSampleRate, fillDistanceSegments, v, SoDerivativeOfCauchyCoordinatesAtEnergySamples, L2); 77 | 78 | ppdif = norm( phipsyIters(:,1:2)-[phi psy], 'fro' ) 79 | P2P_Deformation_Converged = gather(1*( ppdif < 1e-300 )); 80 | P2P_Converged_settings = struct('energy', energy_type, 'p2p_weight', p2p_weight, 'energy_param', energy_parameter); 81 | 82 | statsAll(:, end-1:end) = statsAll(:, end-1:end)/numEnergySamples; % energy normalization 83 | end 84 | 85 | phi = double(phipsyIters(:,1)); 86 | psy = double(phipsyIters(:,2)); 87 | end 88 | 89 | if isempty(P2PCurrentPositions) 90 | Energy_total = 0; E_POSITIONAL = 0; 91 | else 92 | E_POSITIONAL = statsAll(:,end-1); 93 | Energy_total = statsAll(:,end); 94 | E_ISO = Energy_total - E_POSITIONAL; 95 | end 96 | 97 | fprintf('Optimization time: %.4f\n', toc(optimizationTimeOnly)); 98 | 99 | if ~any(strcmpi(solver_type, {'AQP', 'SLIM'})) 100 | XP2PDeform = gather(C*phi + conj(C*psy)); 101 | DeformedP2PhandlePositions = CauchyCoordinatesAtP2Phandles*phi + conj(CauchyCoordinatesAtP2Phandles*psy); 102 | end 103 | 104 | if ~isempty(P2PCurrentPositions) 105 | fprintf('E_ISO: %8.3e, E_POS: %8.3e, E_def: %8.3e\n', [E_ISO E_POSITIONAL Energy_total]'); 106 | fprintf('Total script time:%.4f\n', toc(total_time)); 107 | end 108 | -------------------------------------------------------------------------------- /p2p_harmonic_prep.m: -------------------------------------------------------------------------------- 1 | %% set parameters 2 | 3 | if exist('cage_offset', 'var')~=1 4 | fprintf('setting default parameters for p2p-harmonic deformation\n'); 5 | cage_offset = 1e-1; 6 | numVirtualVertices = 1; 7 | numEnergySamples = 10000; 8 | p2p_weight = 1e5; 9 | end 10 | 11 | numDenseEvaluationSamples = numEnergySamples; 12 | 13 | if exist('p2p_weight', 'var')~=1, p2p_weight = 1e5; end 14 | if exist('numIterations', 'var')~=1, numIterations = 3; end 15 | if exist('energy_parameter', 'var')~=1, energy_parameter = 1; end 16 | if exist('hessianSampleRate', 'var')~=1,hessianSampleRate = .1; end 17 | 18 | 19 | %% 20 | offsetCage = subdivPolyMat(cage, numVirtualVertices-sum( cellfun(@numel, holes) )-1)*polygonOffset(cage, -cage_offset, false); 21 | offsetHoles = cellfun(@(h) polygonOffset(h, -cage_offset, false), holes, 'UniformOutput', false); 22 | holeCenters = reshape(cellfun(@pointInPolygon, holes), 1, []); 23 | 24 | v = [offsetCage, offsetHoles]; 25 | vv = [v{1}; zeros( sum( cellfun(@numel, v(2:end))-2+1 ), 1 )]; 26 | numVirtualVertices = numel(vv); 27 | 28 | %% compute cauchy coordinates and its derivatives for samples 29 | fSampleOnPolygon = @(n, p) subdivPolyMat(p, n)*p; 30 | 31 | fPerimeter = @(x) sum( abs(x-x([2:end 1])) ); 32 | sumCagePerimeter = sum( cellfun(fPerimeter, v) ); 33 | 34 | energySamples = cellfun(@(h) fSampleOnPolygon(ceil(fPerimeter(h)/sumCagePerimeter*numEnergySamples), h), [cage holes], 'UniformOutput', false); 35 | nSamplePerCage = cellfun(@numel, energySamples); 36 | energySamples = cat(1, energySamples{:}); 37 | 38 | nextSampleInSameCage = [2:sum(nSamplePerCage) 1]; % next sample on the same cage, for Lipschitz constants and correct sample spacing computation 39 | nextSampleInSameCage( cumsum(nSamplePerCage) ) = 1+cumsum( [0 nSamplePerCage(1:end-1)] ); 40 | 41 | %% compute D for interpolation 42 | C = cauchyCoordinates(v, X, holeCenters); 43 | D = derivativesOfCauchyCoord(v, X, holeCenters); 44 | 45 | %% check if the offsetted boundary is simple 46 | assert( signedpolyarea( fC2R(cage) ) > 0, 'outer boundary vertex should be ordered in CCW for proper Cauchy coordiates computation!'); 47 | assert( all(cellfun( @(h) signedpolyarea(fC2R(h)), holes )<0), 'inner boundary vertex should be ordered in CW for proper Cauchy coordiates computation!'); 48 | assert( ~any(cellfun(@(x) ~isempty( selfintersect(real(x), imag(x)) ), [offsetCage, offsetHoles])), 'offsetted outer/inner boundary should not selfintersects.'); 49 | 50 | %% 51 | P2P_Deformation_Converged = 0; 52 | NLO_preprocessed = false; 53 | 54 | if ~exist('Phi', 'var') || numel(Phi)~=numVirtualVertices 55 | Phi = vv; 56 | Psy = Phi*0; 57 | end 58 | 59 | phipsyIters = []; 60 | XP2PDeform = gather(C*Phi + conj(C*Psy)); 61 | needsPreprocessing = true; 62 | -------------------------------------------------------------------------------- /p2p_harmonic_savedata.m: -------------------------------------------------------------------------------- 1 | 2 | P2Psrc = X(P2PVtxIds); 3 | P2Pdst = P2PCurrentPositions; 4 | 5 | datafile = fullfile(datadir, 'data.mat'); 6 | if exist(datafile, 'file') == 2 7 | warning( [datafile ' exists! backed up'] ); 8 | movefile(datafile, [datafile '.bak']); 9 | end 10 | 11 | fprintf('saving data to %s\n', datafile); 12 | 13 | if ~exist('P2Psets', 'var'), P2Psets = {struct('src', P2Psrc, 'dst', P2Pdst)}; end 14 | 15 | if ~exist('textureClampingFlag', 'var'), textureClampingFlag = 0; end 16 | if ~exist('textureScale', 'var'), textureScale = 1; end 17 | 18 | save( datafile, 'allcages', 'Phi', 'Psy', 'P2Psrc', 'P2Pdst', 'cage_offset', 'numVirtualVertices', ... 19 | 'numEnergySamples', 'textureScale', 'textureClampingFlag', 'P2Psets', 'img_w', 'img_h'); 20 | 21 | -------------------------------------------------------------------------------- /pointInPolygon.m: -------------------------------------------------------------------------------- 1 | function c = pointInPolygon(b) 2 | 3 | fR2C = @(x) complex(x(:,1), x(:,2)); 4 | if isreal(b), b=fR2C(b); end 5 | 6 | dt = delaunayTriangulation(real(b), imag(b), [1:numel(b); 2:numel(b) 1]'); 7 | 8 | cc = fR2C(dt.circumcenter); 9 | d = abs( b(dt(:,1)) - cc ); 10 | d( ~inpolygon(real(cc), imag(cc), real(b), imag(b)) ) = 0; 11 | 12 | [~, i] = max(d); 13 | c = cc(i); -------------------------------------------------------------------------------- /polygonOffset.m: -------------------------------------------------------------------------------- 1 | function p = polygonOffset(x, d, useRelatived) 2 | 3 | if nargin<3 4 | useRelatived = true; 5 | end 6 | 7 | % dRelative = .2; 8 | % x = mxsub*x0; 9 | 10 | complexInput = ~isreal(x); 11 | if complexInput 12 | x = [real(x) imag(x)]; 13 | end 14 | n = size(x,1); 15 | fRowNorm = @(x) sqrt( sum(x.^2, 2) ); 16 | 17 | %% take care of straight edges 18 | e = x - x([2:end 1],:); 19 | e = complex(e(:,1), e(:,2)); 20 | angles = angle(e); 21 | iscorner = abs( angles - angles([end 1:end-1]) ) > 1e-8; 22 | 23 | if any(~iscorner) 24 | cornids = find(iscorner); 25 | ncorn = numel(cornids); 26 | 27 | prevcorn = cumsum(iscorner); 28 | prevcorn(prevcorn==0) = ncorn; 29 | 30 | prevcornidsX = cornids(prevcorn); 31 | nextcornidsX = cornids(mod(prevcorn, ncorn)+1); 32 | 33 | fDistVij = @(i, j) fRowNorm( x(i,:) - x(j,:) ); 34 | wts = fDistVij(1:n, nextcornidsX)./fDistVij(prevcornidsX, nextcornidsX); 35 | 36 | S = sparse( [1:n 1:n], [prevcorn mod(prevcorn, ncorn)+1], [wts; 1-wts]', n, ncorn); 37 | 38 | x = x(cornids,:); 39 | n = numel(cornids); 40 | end 41 | 42 | %% offset a simple polygon 43 | offs = zeros(n, 2); 44 | 45 | i2 = 1:n; 46 | i1 = [n 1:n-1]; 47 | i3 = [2:n 1]; 48 | A = x(i1,1).*(x(i2,2)-x(i3,2)) + x(i2,1).*(x(i3,2)-x(i1,2)) + x(i3,1).*(x(i1,2)-x(i2,2)); 49 | for i=1:n 50 | e1 = x(i, :) - x(mod(i, n)+1, :); 51 | e2 = x(mod(i+n-2, n)+1, :) - x(i, :); 52 | offs(i,:) = [-e1' e2']*fRowNorm([e2; e1])/A(i); 53 | end 54 | 55 | if useRelatived 56 | d = min( fRowNorm(x-x([2:end 1],:)) )*d; 57 | end 58 | 59 | noff = numel(d); 60 | p = repmat(x, 1, noff) + reshape([offs(:,1)*d; offs(:,2)*d], n, []); 61 | 62 | if complexInput 63 | p = complex( p(:,1:2:end), p(:,2:2:end) ); 64 | end 65 | 66 | %% 67 | if any(~iscorner) 68 | p = S*p; 69 | end 70 | 71 | % fDrawPolygon = @(x) plot(x([1:end 1],1:2:end), x([1:end 1],2:2:end), '-*'); 72 | % figuredocked; fDrawPolygon(x); axis equal; 73 | % hold on; h = fDrawPolygon(p); 74 | % set(h, 'color', 'r'); axis equal; 75 | % 76 | % shownumber(x, 1:n, 'b'); 77 | % shownumber(p); 78 | -------------------------------------------------------------------------------- /selfintersect.m: -------------------------------------------------------------------------------- 1 | function [x0,y0,segments]=selfintersect(x,y) 2 | 3 | %SELFINTERSECT Self-intersections of a curve. 4 | % 5 | % [X0,Y0,SEGMENTS] = SELFINTERSECT(X,Y) computes the locations where 6 | % a curve self-intersects in a fast and robust way. 7 | % The curve can be broken with NaNs or have vertical segments. 8 | % Segments of the curve involved in each of the self-interesections are 9 | % also provided. 10 | % 11 | % Vectors X and Y are equal-length vectors of at least four points defining 12 | % the curve. 13 | % X0 and Y0 are column vectors with the x- and y- coordinates, respectively 14 | % of the N self-intersections. 15 | % SEGMENTS is an N x 2 matrix containing the pairs of segments involved in 16 | % each self-intersection. 17 | % 18 | % This program uses the theory of operation of the file "Fast and Robust Curve 19 | % Intersections" submitted by Douglas M. Schwartz (intersections.m, F.Id: 11837). 20 | % 21 | % Example of use 22 | % N=201; 23 | % th=linspace(-3*pi,4*pi,N); 24 | % R=1; 25 | % x=R*cos(th)+linspace(0,6,N); 26 | % y=R*sin(th)+linspace(0,1,N); 27 | % t0=clock; 28 | % [x0,y0,segments]=selfintersect(x,y) 29 | % etime(clock,t0) 30 | % plot(x,y,'b',x0,y0,'.r'); 31 | % axis ('equal'); grid 32 | 33 | % 34 | % See also INTERSECTIONS. 35 | % 36 | %Version: 1.0, December 11, 2006 37 | %Tested under MATLAB 6.5.0. R13. 38 | % 39 | % (c) Antoni J. Canos. 40 | % ITACA. Techincal University of Valencia (Spain) 41 | % Email: ancama2@dcom.upv.es 42 | 43 | 44 | % Input checks. 45 | narginchk(2,2) 46 | % x and y must be vectors with same number of points (at least 4 for self-intersection). 47 | if sum(size(x) > 3) ~= 1 || sum(size(y) > 3) ~= 1 || ... 48 | length(x) ~= length(y) 49 | error('X and Y must be equal-length vectors of at least 4 points.') 50 | end 51 | 52 | x0=[]; 53 | y0=[]; 54 | segments=[]; 55 | 56 | % Two similar curves are firstly created. 57 | x1=x; x2=x; 58 | y1=y; y2=y; 59 | 60 | x1 = x1(:); 61 | y1 = y1(:); 62 | x2 = x2(:); 63 | y2 = y2(:); 64 | 65 | % Compute number of line segments in each curve and some differences we'll 66 | % need later. 67 | n1 = length(x1) - 1; 68 | n2 = length(x2) - 1; 69 | 70 | dxy1 = diff([x1 y1]); 71 | dxy2 = diff([x2 y2]); 72 | 73 | % Determine the combinations of i and j where the rectangle enclosing the 74 | % i'th line segment of curve 1 overlaps with the rectangle enclosing the 75 | % j'th line segment of curve 2. 76 | [i,j] = find(repmat(min(x1(1:end-1),x1(2:end)),1,n2) <= ... 77 | repmat(max(x2(1:end-1),x2(2:end)).',n1,1) & ... 78 | repmat(max(x1(1:end-1),x1(2:end)),1,n2) >= ... 79 | repmat(min(x2(1:end-1),x2(2:end)).',n1,1) & ... 80 | repmat(min(y1(1:end-1),y1(2:end)),1,n2) <= ... 81 | repmat(max(y2(1:end-1),y2(2:end)).',n1,1) & ... 82 | repmat(max(y1(1:end-1),y1(2:end)),1,n2) >= ... 83 | repmat(min(y2(1:end-1),y2(2:end)).',n1,1)); 84 | 85 | % Removing coincident and adjacent segments. 86 | remove=find(abs(i-j)<2); 87 | i(remove)=[]; 88 | j(remove)=[]; 89 | 90 | % Removing duplicate combinations of segments. 91 | remove=[]; 92 | for ii=1:size(i,1) 93 | ind=find((i(ii)==j(ii:end))&(j(ii)==i(ii:end))); 94 | remove=[remove;ii-1+ind]; 95 | end 96 | i(remove)=[]; 97 | j(remove)=[]; 98 | 99 | % Find segments pairs which have at least one vertex = NaN and remove them. 100 | % This line is a fast way of finding such segment pairs. We take 101 | % advantage of the fact that NaNs propagate through calculations, in 102 | % particular subtraction (in the calculation of dxy1 and dxy2, which we 103 | % need anyway) and addition. 104 | remove = isnan(sum(dxy1(i,:) + dxy2(j,:),2)); 105 | i(remove) = []; 106 | j(remove) = []; 107 | 108 | % Find segments pairs which have at least one vertex = NaN and remove them. 109 | % This line is a fast way of finding such segment pairs. We take 110 | % advantage of the fact that NaNs propagate through calculations, in 111 | % particular subtraction (in the calculation of dxy1 and dxy2, which we 112 | % need anyway) and addition. 113 | remove = isnan(sum(dxy1(i,:) + dxy2(j,:),2)); 114 | i(remove) = []; 115 | j(remove) = []; 116 | 117 | % Initialize matrices. We'll put the T's and B's in matrices and use them 118 | % one column at a time. For some reason, the \ operation below is faster 119 | % on my machine when A is sparse so we'll initialize a sparse matrix with 120 | % the fixed values and then assign the changing values in the loop. 121 | n = length(i); 122 | T = zeros(4,n); 123 | A = sparse([1 2 3 4],[3 3 4 4],-1,4,4,8); 124 | B = -[x1(i) x2(j) y1(i) y2(j)].'; 125 | index_dxy1 = [1 3]; % A(1) = A(1,1), A(3) = A(3,1) 126 | index_dxy2 = [6 8]; % A(6) = A(2,2), A(8) = A(4,2) 127 | 128 | % Loop through possibilities. Set warning not to trigger for anomalous 129 | % results (i.e., when A is singular). 130 | warning_state = warning('off','MATLAB:singularMatrix'); 131 | try 132 | for k = 1:n 133 | A(index_dxy1) = dxy1(i(k),:); 134 | A(index_dxy2) = dxy2(j(k),:); 135 | T(:,k) = A\B(:,k); 136 | end 137 | warning(warning_state) 138 | catch 139 | warning(warning_state) 140 | rethrow(lasterror) 141 | end 142 | 143 | % Find where t1 and t2 are between 0 and 1 and return the corresponding x0 144 | % and y0 values. Anomalous segment pairs can be segment pairs that are 145 | % colinear (overlap) or the result of segments that are degenerate (end 146 | % points the same). The algorithm will return an intersection point that 147 | % is at the center of the overlapping region. Because of the finite 148 | % precision of floating point arithmetic it is difficult to predict when 149 | % two line segments will be considered to overlap exactly or even intersect 150 | % at an end point. For this algorithm, an anomaly is detected when any 151 | % element of the solution (a single column of T) is a NaN. 152 | 153 | in_range = T(1,:) >= 0 & T(2,:) >= 0 & T(1,:) < 1 & T(2,:) < 1; 154 | anomalous = any(isnan(T)); 155 | if any(anomalous) 156 | ia = i(anomalous); 157 | ja = j(anomalous); 158 | % set x0 and y0 to middle of overlapping region. 159 | T(3,anomalous) = (max(min(x1(ia),x1(ia+1)),min(x2(ja),x2(ja+1))) + ... 160 | min(max(x1(ia),x1(ia+1)),max(x2(ja),x2(ja+1))))/2; 161 | T(4,anomalous) = (max(min(y1(ia),y1(ia+1)),min(y2(ja),y2(ja+1))) + ... 162 | min(max(y1(ia),y1(ia+1)),max(y2(ja),y2(ja+1))))/2; 163 | x0 = T(3,in_range | anomalous).'; 164 | y0 = T(4,in_range | anomalous).'; 165 | i=i(in_range | anomalous); 166 | j=j(in_range | anomalous); 167 | else 168 | x0 = T(3,in_range).'; 169 | y0 = T(4,in_range).'; 170 | i=i(in_range); 171 | j=j(in_range); 172 | end 173 | 174 | segments=sort([i,j],2); -------------------------------------------------------------------------------- /signedAreas.m: -------------------------------------------------------------------------------- 1 | function A = signedAreas(x, t, planar) 2 | 3 | if ~isreal(x), x = [real(x) imag(x)]; end 4 | if nargin<3, planar = false; end 5 | 6 | if size(x,2)==2 || range(x(:,3))