├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── .idea └── workspace.xml ├── CMakeLists.txt ├── Cost ├── CMakeLists.txt ├── ConnectednessConstraint.cpp ├── ConnectednessConstraint.h ├── Cost.h ├── DiffuseYamabe.cpp ├── DiffuseYamabe.h ├── EigenHelper.h ├── LinearizedElasticity.cpp ├── LinearizedElasticity.h ├── ModicaMortola.cpp ├── ModicaMortola.h ├── SparseMatrix.cpp ├── SparseMatrix.h ├── StoppingCriteria.cpp ├── StoppingCriteria.h ├── StrangRules.hpp ├── UnionFind.cpp └── UnionFind.h ├── Mesh ├── Bfs.h ├── CMakeLists.txt ├── Dijkstra.h ├── FEM.cpp ├── FEM.h ├── FEM.hpp ├── FastMarchingMethod.cpp ├── FastMarchingMethod.h ├── GraphCommon.h ├── Mesh.cpp ├── Mesh.h ├── MeshElements.cpp ├── MeshElements.h ├── MeshFeature.cpp ├── MeshFeature.h ├── SmoothVertexData.cpp ├── SmoothVertexData.h └── Surface.h ├── Optimization ├── CMakeLists.txt ├── Functional.cpp ├── Functional.h ├── Functional.hpp ├── LbfgsSolver.cpp ├── LbfgsSolver.h ├── LossFunction.cpp ├── LossFunctions.h ├── Optimization.h ├── Problem.cpp ├── Problem.h ├── RecursiveProblem.cpp ├── RecursiveProblem.h ├── Solver.cpp ├── Solver.h ├── Tag.cpp ├── Tag.h ├── Tree.cpp └── Tree.h ├── Phasefield.html ├── README.md ├── ScopedTimer ├── CMakeLists.txt └── include │ └── ScopedTimer │ ├── ScopedTimer.cpp │ └── ScopedTimer.h ├── Utilities ├── Algorithms.h ├── Allocate.h ├── C1Functions.h ├── CMakeLists.txt ├── CircularBuffer.h ├── Enums.h ├── FunctionRef.h ├── HashMap.h ├── Heap.h ├── MinMaxAndDAryHeap.h ├── Range.h ├── Serialize.h ├── SharedPointer.h ├── SmallArray.h ├── SmartEnum.h ├── SparseMatrix.cpp ├── SparseMatrix.h ├── StlAlgorithm.h ├── Traits.h ├── Types.h ├── UniqueFunction.h ├── Utility.h ├── YCombinator.h └── test.cpp ├── VideoSaver ├── CMakeLists.txt ├── VideoSaver.cpp └── VideoSaver.h ├── Viewer ├── ArcBall.cpp ├── ArcBall.h ├── Bvh.cpp ├── Bvh.h ├── CMakeLists.txt ├── PlotCallback.cpp ├── PlotCallback.h ├── SourceSansPro-Regular.ttf ├── Viewer.cpp ├── Viewer.h ├── data │ ├── capsule_high_res.ply │ ├── capsule_high_res_split.bin │ ├── connectedness.conf │ ├── resources.conf │ ├── sphere.bin │ ├── sphere.conf │ ├── sphere.ply │ ├── sphere_connected.conf │ ├── spot.ply │ ├── spot_area.conf │ └── spot_high_res.ply └── resources.conf ├── Visualization ├── CMakeLists.txt ├── ImGuiWidget.cpp ├── ImGuiWidgets.h ├── Paths.cpp ├── Paths.h ├── Upload.cpp ├── Upload.h ├── Visualization.h ├── VisualizationProxy.cpp ├── VisualizationProxy.h ├── primitive_options.cpp └── primitive_options.hpp ├── conanfile.txt ├── contrib └── CMakeLists.txt ├── images └── image.png ├── main.cpp ├── modules ├── FindAssimp.cmake ├── FindCorrade.cmake ├── FindMKL.cmake ├── FindMagnum.cmake ├── FindMagnumIntegration.cmake ├── FindMagnumPlugins.cmake ├── FindOpenGLES2.cmake └── FindSuiteSparse.cmake ├── primitives.conf ├── time-compile └── todos.txt /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ${{ matrix.os }} 7 | strategy: 8 | matrix: 9 | os: [ubuntu-20.04, macos-latest] 10 | config: [Debug] 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | submodules: recursive 15 | - name: Dependencies 16 | if: startsWith(matrix.os,'ubuntu') 17 | run: | 18 | sudo apt-get update 19 | sudo apt-get install xorg-dev libglu1-mesa-dev gcc-10 20 | - name: Generate 21 | run: | 22 | mkdir build 23 | cd build 24 | cmake -DCMAKE_BUILD_TYPE=${{ matrix.config }} .. 25 | cd .. 26 | - name: Build 27 | run: | 28 | cmake --build build/ --parallel --config ${{ matrix.config }} 29 | 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-*/ 2 | build*/ 3 | assets/ 4 | shaders/ 5 | Cost/external/ 6 | conan_modules/* 7 | math/openblas_modules/* 8 | docs/ 9 | log.md 10 | .vscode 11 | .idea/ 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "toolchains"] 2 | path = toolchains 3 | url = git://github.com/mosra/toolchains 4 | [submodule "contrib/magnum-integration"] 5 | path = contrib/magnum-integration 6 | url = https://github.com/mosra/magnum-integration.git 7 | [submodule "contrib/liblbfgs"] 8 | path = contrib/liblbfgs 9 | url = https://github.com/chokkan/liblbfgs.git 10 | [submodule "contrib/magnum"] 11 | path = contrib/magnum 12 | url = https://github.com/mosra/magnum.git 13 | [submodule "contrib/corrade"] 14 | path = contrib/corrade 15 | url = https://github.com/mosra/corrade.git 16 | [submodule "contrib/magnum-plugins"] 17 | path = contrib/magnum-plugins 18 | url = https://github.com/mosra/magnum-plugins.git 19 | [submodule "contrib/implot"] 20 | path = contrib/implot 21 | url = https://github.com/epezent/implot.git 22 | [submodule "contrib/imgui"] 23 | path = contrib/imgui 24 | url = https://github.com/ocornut/imgui.git 25 | [submodule "contrib/bvh"] 26 | path = contrib/bvh 27 | url = https://github.com/madmann91/bvh.git 28 | [submodule "contrib/eigen"] 29 | path = contrib/eigen 30 | url = https://gitlab.com/libeigen/eigen.git 31 | [submodule "contrib/glfw"] 32 | path = contrib/glfw 33 | url = https://github.com/glfw/glfw.git 34 | [submodule "contrib/SuiteSparse"] 35 | path = contrib/SuiteSparse 36 | url = git@github.com:sergiud/SuiteSparse.git 37 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(Phasefield CXX) 3 | 4 | cmake_policy(SET CMP0077 NEW) 5 | 6 | set(CMAKE_CXX_STANDARD 17) 7 | #set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") 8 | #set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") 9 | 10 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/modules) 11 | 12 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 13 | #set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) 14 | 15 | option(PHASEFIELD_WITH_ADOLC "Enable automatica gradient testing" OFF) 16 | option(PHASEFIELD_WITH_CERES "Enables ceres for optimization" OFF) 17 | option(PHASEFIELD_WITH_IPOPT "Enable ipopt for consrained optimization" OFF) 18 | option(PHASEFIELD_WITH_TBB "Enable threading using intel tbb" OFF) 19 | option(PHASEFIELD_WITH_VIDEO "Make videos using ffmpeg" OFF) 20 | 21 | add_subdirectory(contrib) 22 | 23 | find_package(PkgConfig REQUIRED) 24 | find_package(Corrade REQUIRED Containers Utility) 25 | find_package(Magnum REQUIRED GL MeshTools Primitives Shaders Trade DebugTools) 26 | find_package(MagnumIntegration REQUIRED ImGui) 27 | find_package(MagnumPlugins REQUIRED StanfordSceneConverter) 28 | 29 | if(PHASEFIELD_WITH_IO) 30 | pkg_check_modules(libavcodec REQUIRED IMPORTED_TARGET libavcodec) 31 | pkg_check_modules(libavutil REQUIRED IMPORTED_TARGET libavutil) 32 | pkg_check_modules(libswscale REQUIRED IMPORTED_TARGET libswscale) 33 | add_subdirectory(VideoSaver) 34 | endif() 35 | 36 | if(PHASEFIELD_WITH_ADOLC) 37 | #message(FATAL_ERROR "finding adolc") 38 | pkg_check_modules(Adolc REQUIRED IMPORTED_TARGET adolc) 39 | endif() 40 | 41 | if(PHASEFIELD_WITH_IPOPT) 42 | pkg_check_modules(Ipopt REQUIRED IMPORTED_TARGET ipopt) 43 | endif() 44 | 45 | if(PHASEFIELD_WITH_CERES) 46 | find_package(Ceres) 47 | endif() 48 | 49 | if(PHASEFIELD_WITH_TBB) 50 | find_package(TBB) 51 | endif() 52 | 53 | if (CORRADE_TARGET_EMSCRIPTEN) 54 | find_package(Magnum REQUIRED EmscriptenApplication) 55 | #if(NOT TARGET OpenGLES2::OpenGLES2) 56 | # message(FATAL_ERROR "gles2 not found") 57 | #elseif() 58 | # message(FATAL_ERROR "gles2 found") 59 | #endif() 60 | else() 61 | find_package(Magnum REQUIRED GlfwApplication) 62 | endif() 63 | 64 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 65 | 66 | add_subdirectory(Utilities) 67 | add_subdirectory(ScopedTimer) 68 | add_subdirectory(Visualization) 69 | add_subdirectory(Optimization) 70 | add_subdirectory(Mesh) 71 | add_subdirectory(Cost) 72 | add_subdirectory(Viewer) 73 | 74 | add_executable(Phasefield main.cpp) 75 | 76 | target_link_libraries(Phasefield PRIVATE Phasefield::Viewer) 77 | #set_property(TARGET Phasefield PROPERTY INTERPROCEDURAL_OPTIMIZATION True) 78 | #add_subdirectory(tests) 79 | 80 | # For Emscripten copy the boilerplate next to the executable so it can be run 81 | # directly from the build dir; provide an install target as well 82 | if(CORRADE_TARGET_EMSCRIPTEN) 83 | add_custom_command(TARGET Phasefield POST_BUILD 84 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 85 | ${MAGNUM_EMSCRIPTENAPPLICATION_JS} 86 | ${MAGNUM_WEBAPPLICATION_CSS} 87 | $ 88 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 89 | ${CMAKE_CURRENT_SOURCE_DIR}/Phasefield.html 90 | $/Phasefield.html) 91 | 92 | install(FILES Phasefield.html DESTINATION ${MAGNUM_DEPLOY_PREFIX}/Phasefield RENAME index.html) 93 | install(TARGETS Phasefield DESTINATION ${MAGNUM_DEPLOY_PREFIX}/Phasefield) 94 | install(FILES 95 | ${MAGNUM_EMSCRIPTENAPPLICATION_JS} 96 | ${MAGNUM_WEBAPPLICATION_CSS} 97 | DESTINATION ${MAGNUM_DEPLOY_PREFIX}/Phasefield) 98 | install(FILES 99 | $/Phasefield.js.mem 100 | $/Phasefield.wasm 101 | DESTINATION ${MAGNUM_DEPLOY_PREFIX}/Phasefield OPTIONAL) 102 | endif() -------------------------------------------------------------------------------- /Cost/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) 2 | 3 | add_library(Cost STATIC 4 | Cost.h 5 | ModicaMortola.cpp 6 | ModicaMortola.h 7 | ConnectednessConstraint.h 8 | ConnectednessConstraint.cpp 9 | StoppingCriteria.cpp 10 | StoppingCriteria.h 11 | UnionFind.cpp 12 | UnionFind.h 13 | DiffuseYamabe.cpp 14 | DiffuseYamabe.h 15 | LinearizedElasticity.cpp 16 | LinearizedElasticity.h 17 | ) 18 | 19 | target_include_directories(Cost PUBLIC 20 | $) 21 | 22 | target_include_directories(Cost SYSTEM PRIVATE ${Eigen3_INCLUDE_DIRS}) 23 | 24 | target_link_libraries(Cost PRIVATE 25 | ScopedTimer::ScopedTimer 26 | Phasefield::Viewer 27 | ) 28 | 29 | 30 | target_link_libraries(Cost PUBLIC 31 | Phasefield::Utilities 32 | Phasefield::VisualizationProxy 33 | Phasefield::Mesh 34 | Phasefield::Optimization 35 | cholmod 36 | umfpack 37 | ) 38 | 39 | find_package(OpenMP) 40 | if(OpenMP_CXX_FOUND) 41 | target_link_libraries(Cost PUBLIC OpenMP::OpenMP_CXX) 42 | endif() 43 | 44 | set_property(TARGET Cost PROPERTY POSITION_INDEPENDENT_CODE ON) 45 | add_library(Phasefield::Cost ALIAS Cost) 46 | 47 | if (PHASEFIELD_WITH_ADOLC) 48 | target_link_libraries(Cost PRIVATE PkgConfig::Adolc) 49 | target_compile_definitions(Cost PRIVATE PHASEFIELD_WITH_ADOLC) 50 | endif() 51 | 52 | if (PHASEFIELD_WITH_TBB) 53 | target_compile_definitions(Cost PRIVATE PHASEFIELD_WITH_TBB) 54 | endif() 55 | -------------------------------------------------------------------------------- /Cost/ConnectednessConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 21.05.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Functional.h" 8 | #include "Surface.h" 9 | 10 | class adouble; 11 | 12 | namespace Phasefield { 13 | 14 | namespace Mn = Magnum; 15 | namespace Cr = Corrade; 16 | 17 | struct Node; 18 | 19 | struct ConnectednessConstraint { 20 | explicit ConnectednessConstraint(Mesh&); 21 | 22 | template 23 | void operator()(ArrayView parameters, 24 | ArrayView weights, 25 | Scalar& out, 26 | ArrayView gradP, 27 | ArrayView gradW); 28 | 29 | [[nodiscard]] size_t numParameters() const; 30 | [[nodiscard]] static FunctionalType::Value type() { return FunctionalType::ConnectednessConstraint; } 31 | 32 | void drawImGuiOptions(VisualizationProxy&); 33 | 34 | void draw(Node&); 35 | char const* getFormattedInterval(); 36 | void recalculateInterval(); 37 | 38 | void saveParameters(Cr::Utility::ConfigurationGroup&) const; 39 | void loadParameters(Cr::Utility::ConfigurationGroup const&); 40 | 41 | Mesh* mesh; 42 | 43 | int purePhase = 1; 44 | double edge0 = 0, edge1 = 1; 45 | double s = 0.5; 46 | bool drawComponents = false; 47 | bool drawGradient = false; 48 | bool ignoreSmallComponents = false; 49 | double* epsilon = nullptr; 50 | 51 | //double pathThickness = 0.01; 52 | 53 | //Cr::Containers::Array instanceData; //tf from cylinder to path section 54 | //Paths* paths = nullptr; 55 | //bool updateInstanceData = false; 56 | //bool generateLineStrips = false; 57 | //bool updateGrad = false; 58 | //bool updateComponents = false; 59 | //bool updateWs = false; 60 | }; 61 | 62 | 63 | DECLARE_FUNCTIONAL_CONSTRUCTOR(ConnectednessConstraint) 64 | DECLARE_FUNCTIONAL_OPERATOR(ConnectednessConstraint, double) 65 | 66 | #ifdef PHASEFIELD_WITH_ADOLC 67 | DECLARE_FUNCTIONAL_OPERATOR(ConnectednessConstraint, adouble) 68 | #endif 69 | 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Cost/Cost.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 09.05.20. 3 | // 4 | 5 | #pragma once 6 | 7 | /* forward declarations */ 8 | 9 | namespace Phasefield { 10 | 11 | struct UnionFind; 12 | 13 | /* Modica Mortola */ 14 | struct DirichletEnergy; 15 | struct AreaRegularizer; 16 | struct DoubleWellPotential; 17 | 18 | } -------------------------------------------------------------------------------- /Cost/DiffuseYamabe.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 8/25/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Surface.h" 8 | #include "Types.h" 9 | #include "Functional.h" 10 | 11 | class adouble; 12 | 13 | namespace Phasefield { 14 | 15 | SMART_ENUM(EnergyType, size_t, Dirichlet, Hencky) 16 | 17 | struct DiffuseYamabe { 18 | 19 | 20 | explicit DiffuseYamabe(Mesh& m); 21 | 22 | template 23 | void operator()(ArrayView parameters, 24 | ArrayView weights, 25 | Scalar& out, 26 | ArrayView gradP, 27 | ArrayView gradW); 28 | 29 | [[nodiscard]] size_t numParameters() const; 30 | 31 | [[nodiscard]] static FunctionalType::Value type() { return FunctionalType::DiffuseYamabe; } 32 | 33 | void drawImGuiOptions(VisualizationProxy& proxy); 34 | 35 | void saveParameters(Cr::Utility::ConfigurationGroup&) const; 36 | void loadParameters(Cr::Utility::ConfigurationGroup const&); 37 | 38 | bool drawSolution = false; 39 | bool drawSolutionThresholded = false; 40 | bool drawSolutionGradient = false; 41 | bool curvatureRescaling = false; 42 | double lambdaWeight = 1; 43 | 44 | Mesh& mesh; 45 | 46 | double getRescalingFactor(Face f) const; 47 | double getRescalingFactor(Vertex v) const; 48 | 49 | EnergyType::Value energy = EnergyType::Hencky; 50 | bool positivePhase = true; 51 | bool negativePhase = true; 52 | }; 53 | 54 | DECLARE_FUNCTIONAL_CONSTRUCTOR(DiffuseYamabe) 55 | DECLARE_FUNCTIONAL_OPERATOR(DiffuseYamabe, double) 56 | 57 | #ifdef PHASEFIELD_WITH_ADOLC 58 | DECLARE_FUNCTIONAL_OPERATOR(DiffuseYamabe, adouble) 59 | #endif 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /Cost/EigenHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 11/3/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Types.h" 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef PHASEFIELD_WITH_ADOLC 16 | #include 17 | 18 | namespace Eigen { 19 | 20 | template<> struct NumTraits 21 | : NumTraits // permits to get the epsilon, dummy_precision, lowest, highest functions 22 | { 23 | typedef adouble Real; 24 | typedef adouble NonInteger; 25 | typedef adouble Nested; 26 | 27 | enum { 28 | IsComplex = 0, 29 | IsInteger = 0, 30 | IsSigned = 1, 31 | RequireInitialization = 1, 32 | ReadCost = 1, 33 | AddCost = 3, 34 | MulCost = 3 35 | }; 36 | }; 37 | 38 | template<> struct NumTraits 39 | : NumTraits // permits to get the epsilon, dummy_precision, lowest, highest functions 40 | { 41 | typedef adouble Real; 42 | typedef adouble NonInteger; 43 | typedef adouble Nested; 44 | 45 | enum { 46 | IsComplex = 0, 47 | IsInteger = 0, 48 | IsSigned = 1, 49 | RequireInitialization = 1, 50 | ReadCost = 1, 51 | AddCost = 3, 52 | MulCost = 3 53 | }; 54 | }; 55 | 56 | } 57 | 58 | inline const adouble& conj(const adouble& x) { return x; } 59 | inline const adouble& real(const adouble& x) { return x; } 60 | inline adouble imag(const adouble&) { return 0.; } 61 | inline adouble abs(const adouble& x) { return fabs(x); } 62 | inline adouble abs2(const adouble& x) { return x*x; } 63 | 64 | #endif 65 | 66 | namespace Phasefield { 67 | 68 | void handleSolverInfo(Eigen::ComputationInfo info) { 69 | switch(info) { 70 | case Eigen::NumericalIssue: 71 | Debug{} << "Numerical Issue"; 72 | break; 73 | case Eigen::NoConvergence: 74 | Debug{} << "No Convergence"; 75 | break; 76 | case Eigen::InvalidInput: 77 | Debug{} << "Invalid Input"; 78 | break; 79 | case Eigen::Success: 80 | Debug{} << "Solver Successfull"; 81 | break; 82 | } 83 | } 84 | 85 | template 86 | struct SelectSolver { 87 | //using type = Eigen::UmfPackLU>; 88 | using type = Eigen::CholmodSupernodalLLT>; 89 | }; 90 | 91 | #ifdef PHASEFIELD_WITH_ADOLC 92 | template<> 93 | struct SelectSolver { 94 | using type = Eigen::SparseLU>; 95 | }; 96 | #endif 97 | 98 | template 99 | using SolverType = typename SelectSolver::type; 100 | 101 | } -------------------------------------------------------------------------------- /Cost/LinearizedElasticity.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 8/25/20. 3 | // 4 | 5 | #include "EigenHelper.h" 6 | #include "LinearizedElasticity.h" 7 | #include "Mesh.h" 8 | #include "C1Functions.h" 9 | #include "Tree.h" 10 | #include "Functional.hpp" 11 | #include "FEM.hpp" 12 | #include "VisualizationProxy.h" 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | namespace Phasefield { 21 | 22 | LinearizedElasticity::LinearizedElasticity(Mesh& m) : mesh(m), 23 | displacementsX(m.vertexCount()), displacementsY(m.vertexCount()), positions(m.vertexCount()) 24 | { 25 | mesh.requireIntegralOperator(); 26 | mesh.requireGradientOperator(); 27 | 28 | for(Vertex v : mesh.vertices()) 29 | positions[v] = Vector2d{v.position().xy()}; 30 | 31 | for(size_t i = 0; i < neumannBoundary.size(); ++i) { 32 | Vertex v = neumannBoundary[i]; 33 | HalfEdge hes[2]; 34 | size_t j = 0; 35 | for(HalfEdge he : v.outgoingHalfEdges()) { 36 | if(he.edge().onBoundaryLoop()) 37 | hes[j++] = he; 38 | } 39 | CORRADE_ASSERT(j == 3, "More than two boundary edge at vertex",); 40 | double boundaryElement = 0.5*(hes[0].asVector().length() + hes[1].asVector().length()); 41 | neumannElements[i] = boundaryElement; 42 | } 43 | } 44 | 45 | double LinearizedElasticity::surfaceLoad() { 46 | const Vector2d force{0., -forceNorm}; 47 | 48 | double load = 0; 49 | 50 | for(size_t i = 0; i < neumannBoundary.size(); ++i) { 51 | Vertex v = neumannBoundary[i]; 52 | Vector2d displacement{displacementsX[v], displacementsY[v]}; 53 | load += neumannElements[i]*Math::dot(force, displacement); 54 | } 55 | 56 | return load; 57 | } 58 | 59 | template 60 | void LinearizedElasticity::operator()( 61 | ArrayView parameters, ArrayView weights, Scalar& out, 62 | ArrayView gradP, ArrayView gradW) { 63 | 64 | 65 | } 66 | 67 | size_t LinearizedElasticity::numParameters() const { return mesh.vertexCount(); } 68 | 69 | void LinearizedElasticity::drawImGuiOptions(VisualizationProxy& proxy) { 70 | bool redraw = false; 71 | 72 | if(ImGui::Checkbox("Apply Displacements", &applyDisplacements)) { 73 | if(applyDisplacements) { 74 | proxy.setCallbacks( 75 | [this, &proxy](Node) { 76 | for(Vertex v : mesh.vertices()) 77 | mesh.position(v).xy() += Vector2(displacementsX[v], displacementsY[v]); 78 | }, 79 | [this]{ applyDisplacements = false; }); 80 | 81 | } else { 82 | for(Vertex v : mesh.vertices()) 83 | mesh.position(v).xy() += Vector2{positions[v]}; 84 | } 85 | redraw = true; 86 | } 87 | 88 | static const double min = 0.0001; 89 | static const double max = 1; 90 | if(ImGui::DragScalar("Poisson Ratio", ImGuiDataType_Double, &poissonRatio, 1.f, &min, &max, "%f", 1)) { 91 | if(applyDisplacements) { 92 | computeDisplacements(); 93 | redraw = true; 94 | } 95 | } 96 | 97 | if(ImGui::DragScalar("Youngs Modulus", ImGuiDataType_Double, &youngModulus, 1.f, &min, &max, "%f", 1)) { 98 | if(applyDisplacements) { 99 | computeDisplacements(); 100 | redraw = true; 101 | } 102 | } 103 | 104 | if(redraw) proxy.redraw(); 105 | } 106 | 107 | DEFINE_FUNCTIONAL_CONSTRUCTOR(LinearizedElasticity) 108 | DEFINE_FUNCTIONAL_OPERATOR(LinearizedElasticity, double) 109 | 110 | #ifdef PHASEFIELD_WITH_ADOLC 111 | DEFINE_FUNCTIONAL_OPERATOR(LinearizedElasticity, adouble) 112 | #endif 113 | 114 | } 115 | -------------------------------------------------------------------------------- /Cost/LinearizedElasticity.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 8/25/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Mesh.h" 8 | #include "Surface.h" 9 | #include "Types.h" 10 | #include "Functional.h" 11 | 12 | #include 13 | 14 | class adouble; 15 | 16 | namespace Phasefield { 17 | 18 | struct LinearizedElasticity { 19 | 20 | explicit LinearizedElasticity(Mesh& mesh); 21 | 22 | template 23 | void operator()(ArrayView parameters, 24 | [[maybe_unused]] ArrayView weights, 25 | Scalar& out, 26 | ArrayView gradP, 27 | [[maybe_unused]] ArrayView gradW); 28 | 29 | [[nodiscard]] size_t numParameters() const; 30 | 31 | [[nodiscard]] static FunctionalType::Value type() { return FunctionalType::DiffuseYamabe; } 32 | 33 | void drawImGuiOptions(VisualizationProxy& proxy); 34 | 35 | double surfaceLoad(); 36 | void computeDisplacements(); 37 | 38 | double youngModulus; 39 | double poissonRatio; 40 | double forceNorm; 41 | 42 | VertexData displacementsX, displacementsY; 43 | VertexData positions; 44 | 45 | Array neumannBoundary; 46 | Array dirichletBoundary; 47 | 48 | Array neumannElements; 49 | Array outwardNormals; 50 | 51 | bool applyDisplacements; 52 | 53 | Mesh& mesh; 54 | }; 55 | 56 | DECLARE_FUNCTIONAL_CONSTRUCTOR(LinearizedElasticity) 57 | DECLARE_FUNCTIONAL_OPERATOR(LinearizedElasticity, double) 58 | 59 | #ifdef PHASEFIELD_WITH_ADOLC 60 | DECLARE_FUNCTIONAL_OPERATOR(LinearizedElasticity, adouble) 61 | #endif 62 | 63 | } 64 | 65 | -------------------------------------------------------------------------------- /Cost/ModicaMortola.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 2/23/20. 3 | // 4 | #include "ModicaMortola.h" 5 | #include "C1Functions.h" 6 | #include "Mesh.h" 7 | #include "Functional.hpp" 8 | 9 | #include 10 | #include 11 | 12 | #ifdef PHASEFIELD_WITH_ADOLC 13 | #include 14 | #endif 15 | 16 | namespace Phasefield { 17 | 18 | DirichletEnergy::DirichletEnergy(Mesh& m) : mesh(m) { 19 | mesh.requireFaceInformation(); 20 | mesh.requireGradientOperator(); 21 | } 22 | 23 | size_t DirichletEnergy::numParameters() const { 24 | return mesh.vertexCount(); 25 | } 26 | 27 | template 28 | void DirichletEnergy::operator()(ArrayView parameters, 29 | ArrayView weights, 30 | Scalar& out, 31 | ArrayView gradP, 32 | ArrayView gradW) { 33 | 34 | for(Face face : mesh.faces()) { 35 | Math::Vector3 grad{0}; 36 | Scalar weight{0}; 37 | 38 | for(HalfEdge he : face.halfEdges()) { 39 | Vertex v = he.next().tip(); 40 | grad += parameters[v.idx]*Math::Vector3{mesh.gradient[he]}; 41 | weight += weights[v.idx]; 42 | } 43 | 44 | weight /= Scalar{3}; 45 | 46 | Scalar gradNormSquared = grad.dot(); 47 | //CORRADE_INTERNAL_ASSERT(!Math::isNan(gradNormSquared)); 48 | out += mesh.faceArea[face]*gradNormSquared*weight; 49 | 50 | if(gradP) { 51 | for(HalfEdge he : face.halfEdges()){ 52 | Vertex v = he.next().tip(); 53 | gradP[v.idx] += mesh.faceArea[face]*2*Math::dot(grad, Math::Vector3{mesh.gradient[he]})*weight; 54 | } 55 | } 56 | if(gradW) { 57 | for(HalfEdge he : face.halfEdges()) { 58 | Vertex v = he.next().tip(); 59 | gradW[v.idx] += mesh.faceArea[face]*gradNormSquared/Scalar{3}; 60 | } 61 | } 62 | } 63 | 64 | //Debug{} << Math::abs(result - out); 65 | } 66 | 67 | AreaRegularizer::AreaRegularizer(Mesh& m) : mesh(m) {} 68 | 69 | size_t AreaRegularizer::numParameters() const { return mesh.vertexCount(); } 70 | 71 | template 72 | void AreaRegularizer::operator()(ArrayView parameters, 73 | ArrayView weights, 74 | Scalar& out, 75 | ArrayView gradP, 76 | ArrayView gradW) { 77 | CORRADE_ASSERT(totalArea, "Total Area not initialized", ); 78 | double invTotalArea = 1./(*totalArea); 79 | Scalar integral = 0; 80 | SmootherStep f; 81 | for(Vertex vertex : mesh.vertices()) { 82 | size_t idx = vertex.idx; 83 | integral += f.eval(parameters[idx])*weights[idx]*mesh.integral[vertex]; 84 | if(gradP) { 85 | gradP[idx] += f.grad(parameters[idx])*weights[idx]*mesh.integral[vertex]*invTotalArea; 86 | } 87 | if(gradW) { 88 | gradW[idx] += f.eval(parameters[idx])*mesh.integral[vertex]; 89 | } 90 | } 91 | out += (integral - 0.5*(*totalArea))*invTotalArea; 92 | } 93 | 94 | DoubleWellPotential::DoubleWellPotential(Mesh& m) : mesh(m) { mesh.requireIntegralOperator(); } 95 | 96 | size_t DoubleWellPotential::numParameters() const { return mesh.vertexCount(); } 97 | 98 | template 99 | void DoubleWellPotential::operator()(ArrayView parameters, 100 | ArrayView weights, 101 | Scalar& cost, 102 | ArrayView gradP, 103 | ArrayView gradW) { 104 | 105 | DoubleWell f; 106 | CORRADE_INTERNAL_ASSERT(parameters.size() == weights.size()); 107 | CORRADE_INTERNAL_ASSERT(mesh.integral.size() == weights.size()); 108 | for(Vertex vertex : mesh.vertices()) { 109 | size_t idx = vertex.idx; 110 | CORRADE_INTERNAL_ASSERT(idx < parameters.size()); 111 | cost += f.eval(parameters[idx])*weights[idx]*mesh.integral[vertex]; 112 | //Debug{} << cost; 113 | //Debug{} << idx; 114 | if(gradP) { 115 | gradP[idx] += f.grad(parameters[idx])*weights[idx]*mesh.integral[vertex]; 116 | } 117 | if(gradW) { 118 | gradW[idx] += f.eval(parameters[idx])*mesh.integral[idx]; 119 | } 120 | } 121 | } 122 | 123 | HierarchicalRegularization::HierarchicalRegularization(Mesh& m) : mesh(m) { mesh.requireIntegralOperator(); } 124 | 125 | size_t HierarchicalRegularization::numParameters() const { return mesh.vertexCount(); } 126 | 127 | template 128 | void HierarchicalRegularization::operator()(ArrayView parameters, 129 | ArrayView weights, 130 | Scalar& out, 131 | ArrayView gradP, 132 | ArrayView gradW) { 133 | for(Vertex vertex : mesh.vertices()) { 134 | const size_t idx = vertex.idx; 135 | const Scalar sq = 0.5*parameters[idx]*parameters[idx]; 136 | out += (1. - weights[idx])*sq*mesh.integral[vertex]; 137 | if(gradP) { 138 | gradP[idx] += (1. - weights[idx])*parameters[idx]*mesh.integral[vertex]; 139 | } 140 | } 141 | } 142 | 143 | /* explicit instantiations */ 144 | 145 | DEFINE_FUNCTIONAL_CONSTRUCTOR(DirichletEnergy) 146 | DEFINE_FUNCTIONAL_CONSTRUCTOR(AreaRegularizer) 147 | DEFINE_FUNCTIONAL_CONSTRUCTOR(DoubleWellPotential) 148 | DEFINE_FUNCTIONAL_CONSTRUCTOR(HierarchicalRegularization) 149 | 150 | DEFINE_FUNCTIONAL_OPERATOR(DirichletEnergy, double) 151 | DEFINE_FUNCTIONAL_OPERATOR(AreaRegularizer, double) 152 | DEFINE_FUNCTIONAL_OPERATOR(DoubleWellPotential, double) 153 | DEFINE_FUNCTIONAL_OPERATOR(HierarchicalRegularization, double) 154 | 155 | #ifdef PHASEFIELD_WITH_ADOLC 156 | DEFINE_FUNCTIONAL_OPERATOR(DirichletEnergy, adouble) 157 | DEFINE_FUNCTIONAL_OPERATOR(AreaRegularizer, adouble) 158 | DEFINE_FUNCTIONAL_OPERATOR(DoubleWellPotential, adouble) 159 | DEFINE_FUNCTIONAL_OPERATOR(HierarchicalRegularization, adouble) 160 | #endif 161 | 162 | } -------------------------------------------------------------------------------- /Cost/ModicaMortola.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 27.11.19. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Types.h" 8 | #include "Surface.h" 9 | #include "Functional.h" 10 | 11 | class adouble; 12 | 13 | namespace Phasefield { 14 | 15 | struct DirichletEnergy { 16 | 17 | explicit DirichletEnergy(Mesh& mesh); 18 | 19 | [[nodiscard]] size_t numParameters() const; 20 | 21 | [[nodiscard]] static FunctionalType::Value type() { return FunctionalType::DirichletEnergy; } 22 | 23 | template 24 | void operator()(ArrayView parameters, 25 | ArrayView weights, 26 | Scalar& out, 27 | ArrayView gradP, 28 | ArrayView gradW); 29 | 30 | Mesh& mesh; 31 | }; 32 | 33 | struct AreaRegularizer { 34 | 35 | explicit AreaRegularizer(Mesh& mesh); 36 | 37 | template 38 | void operator()(ArrayView parameters, 39 | ArrayView weights, 40 | Scalar& out, 41 | ArrayView gradP, 42 | ArrayView gradW); 43 | 44 | [[nodiscard]] size_t numParameters() const; 45 | 46 | [[nodiscard]] static FunctionalType::Value type() { return FunctionalType::AreaRegularizer; } 47 | 48 | double* totalArea = nullptr; 49 | Mesh& mesh; 50 | }; 51 | 52 | struct HierarchicalRegularization { 53 | 54 | explicit HierarchicalRegularization(Mesh& mesh); 55 | 56 | template 57 | void operator()(ArrayView parameters, 58 | ArrayView weights, 59 | Scalar& out, 60 | ArrayView gradP, 61 | ArrayView gradW); 62 | 63 | [[nodiscard]] size_t numParameters() const; 64 | 65 | [[nodiscard]] static FunctionalType::Value type() { return FunctionalType::HierarchicalRegularization; } 66 | 67 | Mesh& mesh; 68 | }; 69 | 70 | struct DoubleWellPotential { 71 | 72 | explicit DoubleWellPotential(Mesh& mesh); 73 | 74 | template 75 | void operator()(ArrayView parameters, 76 | ArrayView weights, 77 | Scalar& out, 78 | ArrayView gradP, 79 | ArrayView gradW); 80 | 81 | [[nodiscard]] size_t numParameters() const; 82 | 83 | [[nodiscard]] static FunctionalType::Value type() { return FunctionalType::DoubleWellPotential; } 84 | 85 | Mesh& mesh; 86 | }; 87 | 88 | DECLARE_FUNCTIONAL_CONSTRUCTOR(DirichletEnergy) 89 | DECLARE_FUNCTIONAL_CONSTRUCTOR(AreaRegularizer) 90 | DECLARE_FUNCTIONAL_CONSTRUCTOR(DoubleWellPotential) 91 | DECLARE_FUNCTIONAL_CONSTRUCTOR(HierarchicalRegularization) 92 | 93 | DECLARE_FUNCTIONAL_OPERATOR(DirichletEnergy, double) 94 | DECLARE_FUNCTIONAL_OPERATOR(AreaRegularizer, double) 95 | DECLARE_FUNCTIONAL_OPERATOR(DoubleWellPotential, double) 96 | DECLARE_FUNCTIONAL_OPERATOR(HierarchicalRegularization, double) 97 | 98 | #ifdef PHASEFIELD_WITH_ADOLC 99 | DECLARE_FUNCTIONAL_OPERATOR(DirichletEnergy, adouble) 100 | DECLARE_FUNCTIONAL_OPERATOR(AreaRegularizer, adouble) 101 | DECLARE_FUNCTIONAL_OPERATOR(DoubleWellPotential, adouble) 102 | DECLARE_FUNCTIONAL_OPERATOR(HierarchicalRegularization, adouble) 103 | #endif 104 | 105 | 106 | } 107 | 108 | 109 | -------------------------------------------------------------------------------- /Cost/SparseMatrix.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 12/14/20. 3 | // 4 | 5 | #include "SparseMatrix.h" 6 | 7 | 8 | SparseMatrix::SparseMatrix(size_t m, size_t n, size_t nnz): 9 | L(*this) 10 | { 11 | data = cholmod_l_spzeros(m, n, nnz, CHOLMOD_REAL, common); 12 | } 13 | 14 | SparseMatrix::SparseMatrix(Triplets& T): 15 | L(*this) 16 | { 17 | cholmod_triplet *triplet = T.toCholmod(); 18 | data = cholmod_l_triplet_to_sparse(triplet, triplet->nnz, common); 19 | } 20 | 21 | SparseMatrix::~SparseMatrix() 22 | { 23 | cholmod_l_free_sparse(&data, common); 24 | data = NULL; 25 | } 26 | 27 | SparseMatrix SparseMatrix::identity(size_t m, size_t n) 28 | { 29 | return SparseMatrix(cholmod_l_speye(m, n, CHOLMOD_REAL, common)); 30 | } 31 | 32 | SparseMatrix SparseMatrix::transpose() const 33 | { 34 | return SparseMatrix(cholmod_l_transpose(data, 1, common)); 35 | } 36 | 37 | size_t SparseMatrix::nRows() const 38 | { 39 | return data->nrow; 40 | } 41 | 42 | size_t SparseMatrix::nCols() const 43 | { 44 | return data->ncol; 45 | } 46 | 47 | size_t SparseMatrix::nnz() const 48 | { 49 | return cholmod_l_nnz(data, common); 50 | } 51 | 52 | double SparseMatrix::norm(int norm) const 53 | { 54 | return cholmod_l_norm_sparse(data, norm, common); 55 | } 56 | 57 | cholmod_sparse* SparseMatrix::toCholmod() 58 | { 59 | return data; 60 | } 61 | 62 | void scale(double s, cholmod_sparse *A) 63 | { 64 | // A = s*A 65 | DenseMatrix S(1, 1); 66 | S(0, 0) = s; 67 | cholmod_l_scale(S.toCholmod(), CHOLMOD_SCALAR, A, common); 68 | } 69 | 70 | cholmod_sparse* add(cholmod_sparse *A, cholmod_sparse *B, double alpha[2], double beta[2]) 71 | { 72 | // C = alpha*A + beta*B 73 | return cholmod_l_add(A, B, alpha, beta, 1, 1, common); 74 | } 75 | 76 | cholmod_sparse* mul(cholmod_sparse *A, cholmod_sparse *B) 77 | { 78 | // C = A*B 79 | return cholmod_l_ssmult(A, B, 0, 1, 1, common); 80 | } 81 | 82 | void mul(cholmod_sparse *A, cholmod_dense *X, cholmod_dense *Y, double alpha[2], double beta[2]) 83 | { 84 | // Y = alpha*(A*X) + beta*Y 85 | cholmod_l_sdmult(A, 0, alpha, beta, X, Y, common); 86 | } 87 | 88 | SparseMatrix operator*(const SparseMatrix& A, double s) 89 | { 90 | cholmod_sparse *data = A.copy(); 91 | scale(s, data); 92 | 93 | return SparseMatrix(data); 94 | } 95 | 96 | SparseMatrix operator+(const SparseMatrix& A, const SparseMatrix& B) 97 | { 98 | double alpha[2] = {1.0, 1.0}; 99 | double beta[2] = {1.0, 1.0}; 100 | return SparseMatrix(add(A.data, B.data, alpha, beta)); 101 | } 102 | 103 | SparseMatrix operator-(const SparseMatrix& A, const SparseMatrix& B) 104 | { 105 | double alpha[2] = {1.0, 1.0}; 106 | double beta[2] = {-1.0, -1.0}; 107 | return SparseMatrix(add(A.data, B.data, alpha, beta)); 108 | } 109 | 110 | SparseMatrix operator*(const SparseMatrix& A, const SparseMatrix& B) 111 | { 112 | return SparseMatrix(mul(A.data, B.data)); 113 | } 114 | 115 | DenseMatrix operator*(const SparseMatrix& A, const DenseMatrix& X) 116 | { 117 | DenseMatrix Y(A.nRows(), X.nCols()); 118 | double alpha[2] = {1.0, 1.0}; 119 | double beta[2] = {0.0, 0.0}; 120 | mul(A.data, X.data, Y.data, alpha, beta); 121 | 122 | return Y; 123 | } 124 | 125 | SparseMatrix& operator*=(SparseMatrix& A, double s) 126 | { 127 | scale(s, A.data); 128 | A.L.clearNumeric(); 129 | 130 | return A; 131 | } 132 | 133 | SparseMatrix& operator+=(SparseMatrix& A, const SparseMatrix& B) 134 | { 135 | double alpha[2] = {1.0, 1.0}; 136 | double beta[2] = {1.0, 1.0}; 137 | A = add(A.data, B.data, alpha, beta); 138 | 139 | return A; 140 | } 141 | 142 | SparseMatrix& operator-=(SparseMatrix& A, const SparseMatrix& B) 143 | { 144 | double alpha[2] = {1.0, 1.0}; 145 | double beta[2] = {-1.0, -1.0}; 146 | A = add(A.data, B.data, alpha, beta); 147 | 148 | return A; 149 | } 150 | 151 | void SparseMatrix::clear() 152 | { 153 | 154 | } 155 | 156 | Triplets::Triplets(size_t m_, size_t n_): 157 | m(m_), 158 | n(n_), 159 | capacity(m_) 160 | { 161 | data = cholmod_l_allocate_triplet(m, n, capacity, 0, CHOLMOD_REAL, common); 162 | data->nnz = 0; 163 | } 164 | 165 | Triplets::~Triplets() 166 | { 167 | clear(); 168 | } 169 | 170 | void Triplets::add(size_t i, size_t j, double x) 171 | { 172 | if (data->nnz == capacity) increaseCapacity(); 173 | 174 | ((size_t *)data->i)[data->nnz] = i; 175 | ((size_t *)data->j)[data->nnz] = j; 176 | ((double *)data->x)[data->nnz] = x; 177 | data->nnz++; 178 | } 179 | 180 | cholmod_triplet* Triplets::toCholmod() 181 | { 182 | return data; 183 | } 184 | 185 | void Triplets::increaseCapacity() 186 | { 187 | // create triplet with increased capacity 188 | capacity *= 2; 189 | cholmod_triplet *newData = cholmod_l_allocate_triplet(m, n, capacity, 0, CHOLMOD_REAL, common); 190 | memcpy(newData->i, data->i, data->nzmax*sizeof(size_t)); 191 | memcpy(newData->j, data->j, data->nzmax*sizeof(size_t)); 192 | memcpy(newData->x, data->x, data->nzmax*sizeof(double)); 193 | newData->nnz = data->nnz; 194 | 195 | // clear old triplet and assign the newly created triplet to it 196 | clear(); 197 | data = newData; 198 | } 199 | 200 | void Triplets::clear() 201 | { 202 | cholmod_l_free_triplet(&data, common); 203 | data = NULL; 204 | } 205 | -------------------------------------------------------------------------------- /Cost/SparseMatrix.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 12/14/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | class Triplets; 10 | 11 | class SparseMatrix { 12 | public: 13 | SparseMatrix(size_t m = 0, size_t n = 0, size_t nnz = 0); 14 | 15 | explicit SparseMatrix(Triplets& T); 16 | 17 | SparseMatrix(SparseMatrix&& B); 18 | 19 | SparseMatrix& operator=(SparseMatrix&& B); 20 | 21 | ~SparseMatrix(); 22 | 23 | static SparseMatrix identity(size_t m, size_t n); 24 | 25 | SparseMatrix transpose() const; 26 | 27 | size_t nRows() const; 28 | 29 | size_t nCols() const; 30 | 31 | size_t nnz() const; 32 | 33 | // returns norm. 0: Infinity, 1: 1-norm 34 | double norm(int norm) const; 35 | 36 | cholmod_sparse* toCholmod(); 37 | 38 | // math 39 | friend SparseMatrix operator*(const SparseMatrix& A, double s); 40 | friend SparseMatrix operator+(const SparseMatrix& A, const SparseMatrix& B); 41 | friend SparseMatrix operator-(const SparseMatrix& A, const SparseMatrix& B); 42 | friend SparseMatrix operator*(const SparseMatrix& A, const SparseMatrix& B); 43 | 44 | friend SparseMatrix& operator*=(SparseMatrix& A, double s); 45 | friend SparseMatrix& operator+=(SparseMatrix& A, const SparseMatrix& B); 46 | friend SparseMatrix& operator-=(SparseMatrix& A, const SparseMatrix& B); 47 | 48 | protected: 49 | // clear 50 | void clear(); 51 | 52 | // member 53 | cholmod_sparse *data; 54 | }; 55 | 56 | class Triplets { 57 | public: 58 | // constructor 59 | Triplets(size_t m, size_t n); 60 | 61 | // destructor 62 | ~Triplets(); 63 | 64 | // add entry 65 | void add(size_t i, size_t j, double x); 66 | 67 | // returns choldmod representation 68 | cholmod_triplet* toCholmod(); 69 | 70 | protected: 71 | // increases capacity 72 | void increaseCapacity(); 73 | 74 | // clear 75 | void clear(); 76 | 77 | // member 78 | cholmod_triplet *data; 79 | size_t m, n, capacity; 80 | }; -------------------------------------------------------------------------------- /Cost/StoppingCriteria.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 22.04.20. 3 | // 4 | 5 | #include "StoppingCriteria.h" 6 | #include "Mesh.h" 7 | 8 | #include 9 | 10 | using namespace Corrade; 11 | 12 | namespace Phasefield { 13 | 14 | using Cr::Utility::formatString; 15 | 16 | StoppingCriteria::StoppingCriteria(Face source, size_t numComponents, FaceData& components) : 17 | m_startComponent(components[source]), 18 | m_components(&components), 19 | m_found(DirectInit, numComponents, false), 20 | m_componentsFound(components[source] + 1), 21 | m_targetVertices(DirectInit, numComponents, Invalid) 22 | { 23 | CORRADE_ASSERT(m_startComponent != Invalid, "Stopping Criteria: source component is not valid",); 24 | } 25 | 26 | bool StoppingCriteria::operator()(Face target) { 27 | size_t comp = (*m_components)[target]; 28 | 29 | if(comp == Invalid || comp <= m_startComponent || m_found[comp]) 30 | return false; 31 | 32 | m_found[comp] = true; 33 | m_targetVertices[comp] = target; 34 | bool stop = ++m_componentsFound == m_found.size(); 35 | return stop; 36 | } 37 | 38 | void StoppingCriteria::checkIfFoundAll() const { 39 | for(size_t i = m_startComponent + 1; i < m_found.size(); ++i) { 40 | CORRADE_ASSERT(m_found[i], formatString("Dijkstra did not reach component {} (total {} components) starting from {}", i, m_found.size(), m_startComponent).c_str(),); 41 | } 42 | } 43 | 44 | Face StoppingCriteria::target(size_t i) const { 45 | CORRADE_INTERNAL_ASSERT(i > m_startComponent); 46 | Face target = m_targetVertices[i]; 47 | CORRADE_INTERNAL_ASSERT(target); 48 | return target; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Cost/StoppingCriteria.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 2/23/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Types.h" 8 | #include "Surface.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace Cr = Corrade; 14 | 15 | namespace Phasefield { 16 | 17 | struct StoppingCriteria { 18 | StoppingCriteria() = default; 19 | 20 | StoppingCriteria(Face source, size_t numComponents, FaceData& components); 21 | 22 | bool operator()(Face target); 23 | 24 | void checkIfFoundAll() const; 25 | 26 | [[nodiscard]] Face target(size_t i) const; 27 | 28 | size_t m_startComponent = 0; 29 | size_t m_componentsFound = 0; 30 | 31 | Array m_found; 32 | Array m_targetVertices; 33 | 34 | FaceData* m_components = nullptr; 35 | }; 36 | 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /Cost/UnionFind.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 2/23/20. 3 | // 4 | 5 | #include "UnionFind.h" 6 | 7 | namespace Phasefield { 8 | 9 | using Node = Implementation::Node; 10 | 11 | UnionFind::UnionFind(size_t n) : Array(NoInit, n) { 12 | for(size_t i = 0; i < n; ++i) { 13 | (*this)[i] = Node{1, i}; 14 | } 15 | } 16 | 17 | size_t& UnionFind::parent(size_t x) { 18 | return (*this)[x].parent; 19 | } 20 | 21 | size_t& UnionFind::rank(size_t x) { 22 | return (*this)[x].rank; 23 | } 24 | 25 | 26 | size_t UnionFind::find(size_t x) { 27 | size_t root = x; 28 | while(parent(root) != root) { 29 | root = parent(root); 30 | } 31 | 32 | while(parent(x) != root) { 33 | auto p = parent(x); 34 | (*this)[x].parent = root; 35 | x = p; 36 | } 37 | 38 | return root; 39 | } 40 | 41 | void UnionFind::unite(size_t x, size_t y) { 42 | auto xRoot = find(x); 43 | auto yRoot = find(y); 44 | 45 | if(xRoot == yRoot) 46 | return; 47 | 48 | if(rank(xRoot) < rank(yRoot)) 49 | std::swap(xRoot, yRoot); 50 | 51 | parent(yRoot) = xRoot; // 52 | if(rank(xRoot) == rank(yRoot)) 53 | ++rank(xRoot); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /Cost/UnionFind.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 2/23/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Types.h" 8 | #include 9 | 10 | namespace Phasefield { 11 | 12 | namespace Implementation { 13 | struct Node { 14 | size_t rank; 15 | size_t parent; 16 | }; 17 | 18 | } 19 | 20 | struct UnionFind : Array { 21 | 22 | explicit UnionFind(size_t n); 23 | 24 | [[nodiscard]] size_t& parent(size_t x); 25 | 26 | [[nodiscard]] size_t& rank(size_t x); 27 | 28 | size_t find(size_t x); 29 | 30 | void unite(size_t x, size_t y); 31 | 32 | }; 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Mesh/Bfs.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 03.03.20. 3 | // 4 | 5 | 6 | #pragma once 7 | 8 | #include "GraphCommon.h" 9 | #include "CircularBuffer.h" 10 | #include "Mesh.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace Phasefield { 16 | 17 | template 18 | class Bfs { 19 | public: 20 | 21 | using VertexType = V; 22 | using EdgeType = std::conditional_t, Edge, DualEdge>; 23 | 24 | explicit Bfs() = default; 25 | 26 | explicit Bfs(Mesh const& mesh) : m_mesh(&mesh) { 27 | update(); 28 | } 29 | 30 | void setSource(V source) { 31 | m_queue.emplace(source); 32 | m_dist[source] = 0; 33 | } 34 | 35 | void reset() { 36 | m_queue = std::queue(); 37 | for(EdgeType& prev : m_shortestPaths) prev = EdgeType{Invalid, m_mesh}; 38 | for(size_t& dist : m_dist) dist = Invalid; 39 | } 40 | 41 | void update() { 42 | size_t n = std::is_same_v ? m_mesh->faceCount() : m_mesh->vertexCount(); 43 | arrayResize(m_dist, NoInit, n); 44 | arrayResize(m_shortestPaths, NoInit, n); 45 | reset(); 46 | } 47 | 48 | auto getEdges(VertexType node) { 49 | if constexpr (std::is_same_v) return node.edges(); 50 | if constexpr (std::is_same_v) { return node.dualEdges(); } 51 | } 52 | 53 | auto getNeighbor(VertexType v, EdgeType edge) { 54 | if constexpr (std::is_same_v) return edge.otherVertex(v); 55 | if constexpr (std::is_same_v) return edge.otherFace(v); 56 | } 57 | 58 | bool step(V& v) { 59 | if(m_queue.empty()) return false; 60 | 61 | v = m_queue.front(); 62 | m_queue.pop(); 63 | size_t distance = m_dist[v]; 64 | 65 | for(EdgeType e : getEdges(v)) { 66 | if constexpr (std::is_same_v) CORRADE_ASSERT(e.isValid(), "Dual Edge not valid", false); 67 | VertexType neighbor = getNeighbor(v, e); 68 | size_t relaxedDist = 1 + distance; 69 | if(relaxedDist < m_dist[neighbor]) { 70 | m_dist[neighbor] = relaxedDist; 71 | m_shortestPaths[neighbor] = e; 72 | m_queue.emplace(neighbor); 73 | } 74 | } 75 | 76 | return true; 77 | } 78 | 79 | template 80 | inline void run(F&& ... cbs) { 81 | V v; 82 | double d; 83 | while(step(v, d)) { 84 | if((cbs(v) || ... )) 85 | break; 86 | } 87 | } 88 | 89 | Graph::ReversedShortestPath getShortestPathReversed(V start, V target) const { 90 | return {{target, this}, {start, this}}; 91 | } 92 | 93 | bool visited(VertexType v) { return m_dist[v] != Invalid; } 94 | 95 | private: 96 | friend Graph::ReversedPathIterator; 97 | 98 | Mesh const* m_mesh = nullptr; 99 | 100 | std::queue m_queue; 101 | MeshData m_dist; 102 | MeshData m_shortestPaths; 103 | }; 104 | 105 | } -------------------------------------------------------------------------------- /Mesh/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) 2 | 3 | add_library(Mesh STATIC 4 | Mesh.cpp 5 | Mesh.h 6 | MeshElements.cpp 7 | MeshElements.h 8 | MeshFeature.cpp 9 | MeshFeature.h 10 | FastMarchingMethod.cpp 11 | FastMarchingMethod.h 12 | Surface.h 13 | Dijkstra.h 14 | Bfs.h 15 | GraphCommon.h 16 | FEM.cpp 17 | FEM.h 18 | ) 19 | 20 | target_include_directories(Mesh PUBLIC 21 | $) 22 | 23 | target_link_libraries(Mesh PRIVATE 24 | ScopedTimer::ScopedTimer 25 | Corrade::Containers 26 | Magnum::GL 27 | Magnum::MeshTools 28 | #Phasefield::Optimization 29 | ) 30 | 31 | target_link_libraries(Mesh PUBLIC 32 | Phasefield::Utilities 33 | Eigen3::Eigen 34 | ) 35 | 36 | add_library(Phasefield::Mesh ALIAS Mesh) 37 | -------------------------------------------------------------------------------- /Mesh/Dijkstra.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 10.12.19. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "GraphCommon.h" 8 | #include "Heap.h" 9 | #include "Mesh.h" 10 | #include "MeshElements.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace Phasefield { 16 | 17 | namespace Cr = Corrade; 18 | 19 | /* 20 | * Dijsktra shortest path algorithm. Can either operate on the face or 21 | * vertex graph induced by the manifold mesh. 22 | */ 23 | template 24 | class Dijkstra { 25 | public: 26 | 27 | using VertexType = V; 28 | using EdgeType = std::conditional_t, Edge, DualEdge>; 29 | 30 | explicit Dijkstra() = default; 31 | 32 | explicit Dijkstra(Mesh const& mesh, MeshData const& weights) : m_mesh(&mesh), m_weights(&weights) { 33 | update(); 34 | } 35 | 36 | void setSource(V source) { 37 | m_heap.emplace(source, 0.); 38 | m_dist[source] = 0; 39 | } 40 | 41 | void reset() { 42 | m_heap.clear(); 43 | for(EdgeType& prev : m_shortestPaths) prev = EdgeType{Invalid, m_mesh}; 44 | for(double& dist : m_dist) dist = std::numeric_limits::infinity(); 45 | } 46 | 47 | void update() { 48 | size_t n = std::is_same_v ? m_mesh->faceCount() : m_mesh->vertexCount(); 49 | arrayResize(m_dist, NoInit, n); 50 | arrayResize(m_shortestPaths, NoInit, n); 51 | reset(); 52 | } 53 | 54 | auto getEdges(VertexType node) { 55 | if constexpr (std::is_same_v) return node.edges(); 56 | if constexpr (std::is_same_v) return node.dualEdges(); 57 | } 58 | 59 | auto getNeighbor(VertexType v, EdgeType edge) { 60 | if constexpr (std::is_same_v) return edge.otherVertex(v); 61 | if constexpr (std::is_same_v) return edge.otherFace(v); 62 | } 63 | 64 | bool step(V& node, double& distance) { 65 | if(m_heap.empty()) return false; 66 | 67 | auto top = m_heap.extractMin(); 68 | node = top.node; 69 | distance = top.distance; 70 | 71 | if(distance > m_dist[node]) 72 | return true; 73 | 74 | for(EdgeType e : getEdges(node)) { 75 | VertexType neighbor = getNeighbor(node, e); 76 | double weight; 77 | if constexpr (std::is_same_v) weight = (*m_weights)[e].getValue(); 78 | else weight = (*m_weights)[e]; 79 | double relaxedDist = weight + distance; 80 | if(relaxedDist < m_dist[neighbor]){ 81 | m_dist[neighbor] = relaxedDist; 82 | m_shortestPaths[neighbor] = e; 83 | m_heap.emplace(neighbor, relaxedDist); 84 | } 85 | } 86 | 87 | return true; 88 | } 89 | 90 | template 91 | inline void run(F&& ... cbs) { 92 | V v; 93 | double d; 94 | while(step(v, d)) { 95 | if((cbs(v) || ... )) 96 | break; 97 | } 98 | } 99 | 100 | Graph::ReversedShortestPath getShortestPathReversed(V start, V target) const { 101 | return {{target, this}, {start, this}}; 102 | } 103 | 104 | private: 105 | friend Graph::ReversedPathIterator; 106 | 107 | /* using pointers to make it default constructible */ 108 | Mesh const* m_mesh = nullptr; 109 | MeshData const* m_weights = nullptr; 110 | 111 | Heap> m_heap; 112 | MeshData m_dist; 113 | MeshData m_shortestPaths; 114 | }; 115 | 116 | } -------------------------------------------------------------------------------- /Mesh/FEM.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/9/20. 3 | // 4 | 5 | #include "FEM.h" 6 | #include "SparseMatrix.h" 7 | #include "Mesh.h" 8 | 9 | #include "FEM.hpp" 10 | 11 | namespace Phasefield { 12 | 13 | void IntegralOperatorFeature::update() { 14 | Mesh& mesh = getMesh(); 15 | 16 | mesh.integral = VertexData{mesh.vertexCount()}; 17 | 18 | for(Face face : mesh.faces()) { 19 | double area = face.area(); 20 | for(Vertex v : face.vertices()) 21 | mesh.integral[v] += area/3.; 22 | } 23 | } 24 | 25 | void MassMatrixFeature::update() { 26 | //Mesh& mesh = getMesh(); 27 | 28 | //Eigen::VectorXd integral{mesh.vertexCount()}; 29 | //integral.setZero(); 30 | //for(Face face : mesh.faces()) { 31 | // double area = face.area(); 32 | // for(Vertex v : face.vertices()) { 33 | // integral[v.idx] += area/3.; 34 | // } 35 | //} 36 | 37 | //if(!mesh.fem) { 38 | // mesh.fem = pointer(); 39 | //} 40 | 41 | //mesh.fem->mass = integral.asDiagonal(); 42 | } 43 | 44 | void GradientFeature::update() { 45 | Mesh& mesh = getMesh(); 46 | 47 | mesh.gradient = HalfEdgeData{mesh.halfEdgeCount()}; 48 | 49 | for(Face face : mesh.faces()) { 50 | Vector3d normal = face.normal(); 51 | double dblA = 2*face.area(); 52 | for(HalfEdge he : face.halfEdges()) { 53 | Vector3d v = he.asVector(); 54 | mesh.gradient[he] = Math::cross(normal, v).normalized()*v.length()/dblA; 55 | } 56 | } 57 | } 58 | 59 | void StiffnessMatrixFeature::update() { 60 | Mesh& mesh = getMesh(); 61 | 62 | mesh.stiffnessElements = HalfEdgeData{mesh.halfEdgeCount()}; 63 | auto& elements = mesh.stiffnessElements; 64 | 65 | for(HalfEdge he : mesh.halfEdges()) { 66 | // TODO: this is somewhat wrong. Maybe he.edge().onBoundaryLoop() would be better? 67 | if(he.onBoundaryLoop()) continue; 68 | HalfEdge he1 = he.next(); 69 | HalfEdge he2 = he1.next(); 70 | elements[he] = Math::dot(mesh.gradient[he1], mesh.gradient[he2])*he.face().area(); 71 | } 72 | 73 | //Array> triplets{12*mesh.faceCount()}; 74 | 75 | //size_t i = 0; 76 | //for(Face face : mesh.faces()) { 77 | // for(HalfEdge he1 : face.halfEdges()) { 78 | // Vertex v = he1.next().tip(); 79 | // for(HalfEdge he2 : face.halfEdges()) { 80 | // Vertex w = he2.next().tip(); 81 | // double value = Math::dot(mesh.gradient[he2], mesh.gradient[he1])*face.area(); 82 | // triplets[i++] = Eigen::Triplet{v.idx, w.idx, value}; 83 | // } 84 | // } 85 | //} 86 | 87 | //Eigen::SparseMatrix stiffness(n,n); 88 | //stiffness.setFromTriplets(triplets.begin(), triplets.end()); 89 | 90 | //if(!mesh.fem) { 91 | // mesh.fem = pointer(); 92 | //} 93 | //mesh.fem->stiffness = std::move(stiffness); 94 | } 95 | 96 | } -------------------------------------------------------------------------------- /Mesh/FEM.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/9/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "MeshFeature.h" 8 | 9 | namespace Phasefield { 10 | 11 | struct IntegralOperatorFeature : public MeshFeature { 12 | using MeshFeature::MeshFeature; 13 | 14 | void update() override; 15 | 16 | FEATURE_NAME("IntegralOperator") 17 | }; 18 | 19 | struct MassMatrixFeature : public MeshFeature { 20 | using MeshFeature::MeshFeature; 21 | 22 | void update() override; 23 | 24 | FEATURE_NAME("MassMatrix") 25 | }; 26 | 27 | struct StiffnessMatrixFeature : public MeshFeature { 28 | using MeshFeature::MeshFeature; 29 | 30 | void update() override; 31 | 32 | FEATURE_NAME("StiffnessMatrix") 33 | }; 34 | 35 | struct GradientFeature : public MeshFeature { 36 | using MeshFeature::MeshFeature; 37 | 38 | void update() override; 39 | 40 | FEATURE_NAME("Gradient") 41 | }; 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /Mesh/FEM.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 10/1/20. 3 | // 4 | 5 | #ifndef PHASEFIELD_FEM_HPP 6 | #define PHASEFIELD_FEM_HPP 7 | 8 | #include 9 | 10 | namespace Phasefield { 11 | 12 | struct FEM { 13 | Eigen::SparseMatrix stiffness; 14 | Eigen::SparseMatrix mass; 15 | }; 16 | 17 | } 18 | 19 | #endif //PHASEFIELD_FEM_HPP 20 | -------------------------------------------------------------------------------- /Mesh/FastMarchingMethod.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 8/17/20. 3 | // taken from geometry central 4 | 5 | #include "FastMarchingMethod.h" 6 | #include "Types.h" 7 | #include "MeshElements.h" 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | namespace Phasefield { 19 | 20 | using namespace Corrade; 21 | using namespace Magnum; 22 | 23 | namespace { 24 | 25 | // The super fun quadratic distance function in the Fast Marching Method on triangle meshes 26 | // TODO parameter c isn't actually defined in paper, so I guessed that it was an error 27 | double eikonalDistanceSubroutine(double a, double b, Radd theta, double dA, double dB) { 28 | if(theta <= Radd{Math::Constants::piHalf() + 0.1}) { 29 | double u = dB - dA; 30 | double cTheta = Math::cos(theta); 31 | double sTheta2 = 1.0 - cTheta*cTheta; 32 | 33 | // Quadratic equation 34 | double quadA = a*a + b*b - 2*a*b*cTheta; 35 | double quadB = 2*b*u*(a*cTheta - b); 36 | double quadC = b*b*(u*u - a*a*sTheta2); 37 | double sqrtVal = std::sqrt(quadB*quadB - 4*quadA*quadC); 38 | // double tVals[] = {(-quadB + sqrtVal) / (2*quadA), // seems to always be the first one 39 | // (-quadB - sqrtVal) / (2*quadA)}; 40 | 41 | double t = (-quadB + sqrtVal)/(2*quadA); 42 | if(u < t && a*cTheta < b*(t - u)/t && b*(t - u)/t < a/cTheta) { 43 | return dA + t; 44 | } else { 45 | return Math::min(b + dA, a + dB); 46 | } 47 | 48 | } 49 | // Custom by Nick to get acceptable results in obtuse triangles without fancy unfolding 50 | else { 51 | //Debug{} << "Obtuse triangle"; 52 | double maxDist = Math::max(dA, dB); // all points on base are less than this far away, by convexity 53 | double c = Math::sqrt(a*a + b*b - 2*a*b*Math::cos(theta)); 54 | double area = 0.5*Math::sin(theta)*a*b; 55 | double altitude = 2*area/c; // distance to base, must be inside triangle since obtuse 56 | double baseDist = maxDist + altitude; 57 | 58 | return Math::min({b + dA, a + dB, baseDist}); 59 | } 60 | } 61 | 62 | 63 | } // namespace 64 | 65 | 66 | FastMarchingMethod::FastMarchingMethod(Mesh& mesh) : 67 | m_mesh(mesh), 68 | m_distances(NoInit, mesh.vertexCount()), 69 | m_finalized(NoInit, mesh.vertexCount()) 70 | { 71 | //CORRADE_ASSERT(mesh.isManifold(), "Mesh needs to be manifold", ); 72 | mesh.requireAngles(); 73 | mesh.requireEdgeLengths(); 74 | reset(); 75 | } 76 | 77 | void FastMarchingMethod::reset(){ 78 | for(auto& x: m_distances) x = std::numeric_limits::infinity(); 79 | std::memset(m_finalized.data(), 0, m_finalized.size()); 80 | m_frontier.clear(); 81 | } 82 | 83 | void FastMarchingMethod::update() { 84 | 85 | m_mesh.requireAngles(); 86 | m_mesh.requireEdgeLengths(); 87 | 88 | arrayResize(m_finalized, NoInit, m_mesh.vertexCount()); 89 | arrayResize(m_distances, NoInit, m_mesh.vertexCount()); 90 | 91 | reset(); 92 | } 93 | 94 | void FastMarchingMethod::setSource(Vertex v){ 95 | m_frontier.emplace(v, 0.); 96 | } 97 | 98 | bool FastMarchingMethod::step(Vertex& vertex, Double& distance) { 99 | 100 | /* get next non finished item */ 101 | do { 102 | if(m_frontier.empty()) return false; 103 | 104 | // Pop the nearest element 105 | auto item = m_frontier.extractMin(); 106 | vertex = item.node; 107 | distance = item.distance; 108 | 109 | } while(m_finalized[vertex]); 110 | 111 | m_distances[vertex] = distance; 112 | m_finalized[vertex] = true; 113 | 114 | // Add any eligible neighbors 115 | for(HalfEdge he : vertex.incomingHalfEdges()) { 116 | Vertex neighbor = he.tail(); 117 | // Add with length 118 | if(!m_finalized[neighbor]) { 119 | double newDist = distance + m_mesh.edgeLength[he.edge()]; 120 | if(newDist < m_distances[neighbor]) { 121 | m_frontier.emplace(neighbor, distance + m_mesh.edgeLength[he.edge()]); 122 | m_distances[neighbor] = newDist; 123 | } 124 | continue; 125 | } 126 | 127 | // Check the third point of the "left" triangle straddling this edge 128 | if(he.isInterior()) { 129 | Vertex newVert = he.next().next().tail(); 130 | if(!m_finalized[newVert]) { 131 | 132 | // Compute the distance 133 | double lenB = m_mesh.edgeLength[he.next().next().edge()]; 134 | double distB = distance; 135 | double lenA = m_mesh.edgeLength[he.next().edge()]; 136 | double distA = m_distances[neighbor]; 137 | Radd theta = m_mesh.angle[he.next().next().corner()]; 138 | double newDist = eikonalDistanceSubroutine(lenA, lenB, Radd{theta}, distA, distB); 139 | 140 | if(newDist < m_distances[newVert]) { 141 | m_frontier.emplace(newVert, newDist); 142 | m_distances[newVert] = newDist; 143 | } 144 | } 145 | } 146 | 147 | // Check the third point of the "right" triangle straddling this edge 148 | HalfEdge heT = he.twin(); 149 | if(heT.isInterior()) { 150 | Vertex newVert = heT.next().next().tail(); 151 | if(!m_finalized[newVert]) { 152 | 153 | // Compute the distance 154 | double lenB = m_mesh.edgeLength[heT.next().edge()]; 155 | double distB = distance; 156 | double lenA = m_mesh.edgeLength[heT.next().next().edge()]; 157 | double distA = m_distances[neighbor]; 158 | Radd theta = m_mesh.angle[heT.next().next().corner()]; 159 | double newDist = eikonalDistanceSubroutine(lenA, lenB, Radd{theta}, distA, distB); 160 | 161 | if(newDist < m_distances[newVert]) { 162 | m_frontier.emplace(newVert, newDist); 163 | m_distances[newVert] = newDist; 164 | } 165 | } 166 | } 167 | } 168 | 169 | return true; 170 | } 171 | 172 | } -------------------------------------------------------------------------------- /Mesh/FastMarchingMethod.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 8/17/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Mesh.h" 8 | #include "MeshElements.h" 9 | #include "Heap.h" 10 | #include "GraphCommon.h" 11 | 12 | #include 13 | 14 | namespace Phasefield { 15 | 16 | namespace Mg = Magnum; 17 | namespace Cr = Corrade; 18 | 19 | class FastMarchingMethod { 20 | public: 21 | 22 | explicit FastMarchingMethod(Mesh&); 23 | 24 | void setSource(Vertex v); 25 | 26 | bool step(Vertex& v, Mg::Double& distance); 27 | 28 | void reset(); 29 | 30 | void update(); 31 | 32 | private: 33 | 34 | Mesh& m_mesh; 35 | Heap> m_frontier; 36 | 37 | VertexData m_distances; 38 | VertexData m_finalized; 39 | 40 | }; 41 | 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Mesh/GraphCommon.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 03.03.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include "MeshElements.h" 9 | 10 | namespace Phasefield::Graph { 11 | 12 | template 13 | struct HeapElement { 14 | 15 | E node; 16 | double distance; 17 | 18 | bool operator == (const HeapElement& other) const { 19 | return distance == other.distance; 20 | } 21 | 22 | bool operator != (const HeapElement& other) const { 23 | return distance != other.distance; 24 | } 25 | 26 | bool operator < (const HeapElement& other) const { 27 | return distance < other.distance; 28 | } 29 | 30 | bool operator > (const HeapElement& other) const { 31 | return distance > other.distance; 32 | } 33 | }; 34 | 35 | template 36 | class ReversedPathIterator { 37 | public: 38 | using EdgeType = typename A::EdgeType; 39 | using VertexType = typename A::VertexType; 40 | 41 | ReversedPathIterator(VertexType v, A const* algo) : m_v(v), m_algo(algo) { 42 | CORRADE_INTERNAL_ASSERT(v); 43 | } 44 | 45 | EdgeType operator*() { 46 | return m_algo->m_shortestPaths[m_v]; 47 | } 48 | 49 | ReversedPathIterator& operator++() { 50 | if constexpr(std::is_same_v) 51 | m_v = m_algo->m_shortestPaths[m_v].otherVertex(m_v); 52 | else if constexpr(std::is_same_v) 53 | m_v = m_algo->m_shortestPaths[m_v].otherFace(m_v); 54 | return *this; 55 | } 56 | 57 | bool operator!=(ReversedPathIterator const& other) const { 58 | return m_v != other.m_v; 59 | } 60 | 61 | private: 62 | VertexType m_v; 63 | A const* m_algo; 64 | }; 65 | 66 | template 67 | class ReversedShortestPath { 68 | public: 69 | using iterator = ReversedPathIterator; 70 | 71 | ReversedShortestPath(const iterator& begin, const iterator& end) : m_begin(begin), m_end(end) {} 72 | 73 | auto begin() { 74 | return m_begin; 75 | } 76 | 77 | auto end() { 78 | return m_end; 79 | } 80 | 81 | private: 82 | ReversedPathIterator m_begin, m_end; 83 | }; 84 | 85 | 86 | 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /Mesh/MeshFeature.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/9/20. 3 | // 4 | 5 | #include "MeshFeature.h" 6 | #include "Mesh.h" 7 | #include "MeshElements.h" 8 | 9 | #include 10 | #include 11 | 12 | namespace Phasefield { 13 | 14 | MeshFeature::MeshFeature(Mesh& mesh, bool ownedByMesh): m_mesh(&mesh), m_ownedByMesh(ownedByMesh) { 15 | arrayAppend(mesh.m_features, this); 16 | } 17 | 18 | Mesh& MeshFeature::getMesh() { return *m_mesh; } 19 | 20 | MeshFeature::~MeshFeature() { 21 | if(!m_ownedByMesh) { /* if the mesh is now owning the feature we deregister the feature */ 22 | size_t j = Invalid; 23 | auto& features = m_mesh->m_features; 24 | for(size_t i = 0; i < features.size(); ++i) { 25 | if(m_mesh->m_features[i] == this) { 26 | j = i; 27 | break; 28 | } 29 | } 30 | if(j != Invalid) { 31 | std::swap(features[j], features.back()); 32 | arrayResize(features, features.size() - 1); 33 | } 34 | } 35 | } 36 | 37 | void AngleFeature::update() { 38 | Mesh& mesh = getMesh(); 39 | mesh.angle = CornerData{mesh.halfEdgeCount()}; 40 | for(Corner corner : mesh.corners()) { 41 | HalfEdge side1 = corner.side1(); 42 | HalfEdge side2 = corner.side2(); 43 | mesh.angle[corner] = Radd{Mg::Math::angle(side1.direction().normalized(), side2.direction().normalized())}; 44 | } 45 | } 46 | 47 | void FaceInformationFeature::update() { 48 | Mesh& mesh = getMesh(); 49 | 50 | mesh.faceArea = FaceData{mesh.faceCount()}; 51 | mesh.faceNormal = FaceData{mesh.faceCount()}; 52 | mesh.faceDiameter = FaceData{mesh.faceCount()}; 53 | mesh.surfaceArea = 0; 54 | 55 | for(Face face : mesh.faces()) { 56 | size_t i = 0; 57 | Vector3 vertices[3]; 58 | for(Vertex v : face.vertices()) vertices[i++] = v.position(); 59 | 60 | Vector3 side1 = vertices[2] - vertices[0]; 61 | Vector3 side2 = vertices[1] - vertices[0]; 62 | Vector3 side3 = vertices[2] - vertices[1]; 63 | 64 | Vector3d normal = Vector3d{Math::cross(side1, side2)}; 65 | 66 | double area = normal.length()*0.5; 67 | mesh.surfaceArea += area; 68 | mesh.faceArea[face] = area; 69 | mesh.faceNormal[face] = normal.normalized(); 70 | mesh.faceDiameter[face] = double(Math::max({side1.length(), side2.length(), side3.length()})); 71 | } 72 | } 73 | 74 | void EdgeLengthFeature::update() { 75 | Mesh& mesh = getMesh(); 76 | mesh.edgeLength = EdgeData{mesh.edgeCount()}; 77 | for(Edge edge : mesh.edges()) 78 | mesh.edgeLength[edge] = double((edge.vertex1().position() - edge.vertex2().position()).length()); 79 | } 80 | 81 | void GaussianCurvatureFeature::update() { 82 | Mesh& mesh= getMesh(); 83 | mesh.requireAngles(); 84 | mesh.gaussianCurvature = VertexData{mesh.vertexCount()}; 85 | mesh.faceCurvature = FaceData{mesh.faceCount()}; 86 | CornerData augmentedAngles{mesh.halfEdgeCount()}; 87 | VertexData angleSums{mesh.vertexCount()}; 88 | 89 | for(Vertex vertex : mesh.vertices()) { 90 | double angleSum{0}; 91 | for(Corner corner : vertex.corners()) { 92 | angleSum += double(corner.angle()); 93 | } 94 | angleSums[vertex] = angleSum; 95 | 96 | if(!vertex.onBoundary()) { 97 | mesh.gaussianCurvature[vertex] = (2.*Mg::Math::Constants::pi() - double(angleSum))/mesh.integral[vertex]; 98 | 99 | for(Corner corner : vertex.corners()) { 100 | augmentedAngles[corner] = 2*Mg::Math::Constants::pi()*double(corner.angle())/double(angleSum); 101 | } 102 | } 103 | } 104 | 105 | for(Vertex vertex : mesh.vertices()) { 106 | if(vertex.onBoundary()) { 107 | double angleSumAverage = 0; 108 | size_t count = 0; 109 | for(Vertex v : vertex.adjacentVertices()) { 110 | if(!v.onBoundary()) { 111 | angleSumAverage += angleSums[v]; 112 | ++count; 113 | } 114 | } 115 | 116 | angleSumAverage /= double(count); 117 | mesh.gaussianCurvature[vertex] = (2.*Mg::Math::Constants::pi() - double(angleSumAverage))/mesh.integral[vertex]; 118 | 119 | for(Corner corner : vertex.corners()) { 120 | augmentedAngles[corner] = 2*Mg::Math::Constants::pi()*double(corner.angle())/double(angleSumAverage); 121 | } 122 | 123 | } 124 | } 125 | 126 | double max= 0; 127 | for(Face f : mesh.faces()) { 128 | double deficit = Math::Constants::pi(); 129 | for(Corner c : f.corners()) { 130 | deficit -= augmentedAngles[c]; 131 | } 132 | max = Math::max(deficit, max); 133 | mesh.faceCurvature[f] = deficit / f.area(); 134 | } 135 | Debug{} << "Max deficit" << max; 136 | 137 | //Debug{} << "SETTING GAUSIAN CURVATURE --- ONLY FOR BLENDER TORUS SUITABLE"; 138 | //for(Vertex v : mesh.vertices()) { 139 | // auto& p = v.position(); 140 | // double x = p.x(); 141 | // double y = p.y(); 142 | // double z = p.z(); 143 | 144 | // double xy2 = 1.-Math::sqrt(x*x + y*y); 145 | // double cosTheta = xy2/Math::sqrt(z*z + xy2*xy2); 146 | // mesh.gaussianCurvature[v] = 2.*cosTheta/(1. + cosTheta*0.25); 147 | //} 148 | } 149 | 150 | void BoundaryInformation::update() { 151 | Mesh& mesh = getMesh(); 152 | 153 | arrayResize(mesh.isOnBoundary, NoInit, mesh.vertexCount()); 154 | 155 | auto onBoundary = [](Vertex v) { 156 | for(HalfEdge he : v.outgoingHalfEdges()) 157 | if(he.onBoundaryLoop()) return true; 158 | return false; 159 | }; 160 | 161 | for(Vertex v : mesh.vertices()) { 162 | bool isOnBoundary = onBoundary(v); 163 | mesh.isOnBoundary[v] = isOnBoundary; 164 | } 165 | } 166 | 167 | } 168 | 169 | 170 | -------------------------------------------------------------------------------- /Mesh/MeshFeature.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/9/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Surface.h" 8 | #include "Types.h" 9 | 10 | namespace Phasefield { 11 | 12 | class MeshFeature { 13 | public: 14 | 15 | explicit MeshFeature(Mesh& mesh, bool ownedByMesh = true); 16 | virtual ~MeshFeature(); 17 | 18 | Mesh& getMesh(); 19 | 20 | virtual void update() = 0; 21 | 22 | [[nodiscard]] bool ownedByMesh() const { return m_ownedByMesh; }; 23 | 24 | virtual char const* name() = 0; 25 | 26 | private: 27 | Mesh* m_mesh = nullptr; 28 | bool m_ownedByMesh; 29 | }; 30 | 31 | #define FEATURE_NAME(n) static constexpr char Name[] = n; \ 32 | char const* name() override { return Name; } 33 | 34 | struct AngleFeature : public MeshFeature { 35 | using MeshFeature::MeshFeature; 36 | void update() override; 37 | 38 | FEATURE_NAME("Angle"); 39 | }; 40 | 41 | struct FaceInformationFeature : public MeshFeature { 42 | using MeshFeature::MeshFeature; 43 | void update() override; 44 | FEATURE_NAME("FaceInformation") 45 | }; 46 | 47 | struct EdgeLengthFeature : public MeshFeature { 48 | using MeshFeature::MeshFeature; 49 | void update() override; 50 | FEATURE_NAME("EdgeLength") 51 | }; 52 | 53 | struct GaussianCurvatureFeature : public MeshFeature { 54 | using MeshFeature::MeshFeature; 55 | void update() override; 56 | FEATURE_NAME("GaussianCurvature") 57 | }; 58 | 59 | struct BoundaryInformation : public MeshFeature { 60 | using MeshFeature::MeshFeature; 61 | void update() override; 62 | FEATURE_NAME("BoundaryInformation") 63 | }; 64 | 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /Mesh/SmoothVertexData.cpp: -------------------------------------------------------------------------------- 1 | #include "SmoothVertexData.h" 2 | #include "Mesh.h" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace Phasefield { 11 | 12 | void smoothVertexData(VertexData& vertexData, Mesh& mesh, double c) { 13 | size_t n = mesh.vertexCount(); 14 | size_t m = mesh.faceCount(); 15 | 16 | Eigen::MatrixXd V(n, 3); 17 | Eigen::MatrixXi F(m, 3); 18 | 19 | for(Vertex v : mesh.vertices()) { 20 | for(size_t i = 0; i < 3; ++i) { 21 | V(v.idx, i) = v.position()[i]; 22 | } 23 | } 24 | 25 | for(Face face : mesh.faces()) { 26 | size_t i = 0; 27 | for(Vertex v : face.vertices()) { 28 | F(face.idx, i++) = v.idx; 29 | } 30 | } 31 | 32 | Eigen::SparseMatrix M; 33 | Eigen::SparseMatrix L; 34 | igl::massmatrix(V, F, igl::MASSMATRIX_TYPE_BARYCENTRIC, M); 35 | igl::cotmatrix(V, F, L); 36 | 37 | { 38 | Eigen::Map b{vertexData.data(), int(n)}; 39 | Eigen::CholmodSupernodalLLT> solver(M - c*L); 40 | Eigen::Map{vertexData.data(), int(n)} = solver.solve(b); 41 | } 42 | 43 | return; 44 | 45 | mesh.requireMassMatrix(); 46 | mesh.requireStiffnessMatrix(); 47 | 48 | auto& stiffness = mesh.stiffnessElements; 49 | auto& mass = mesh.integral; 50 | 51 | Array> triplets; 52 | arrayReserve(triplets, mesh.vertexCount() + mesh.halfEdgeCount()); 53 | 54 | //for(HalfEdge he : mesh.halfEdges()) { 55 | // if(he.edge().onBoundaryLoop()) continue; 56 | // arrayAppend(triplets, InPlaceInit, he.tail().idx, he.tip().idx, stiffness[he]); 57 | //} 58 | 59 | Eigen::VectorXd b(n); 60 | 61 | for(Vertex v : mesh.vertices()) { 62 | b[v.idx] = vertexData[v]; 63 | arrayAppend(triplets, InPlaceInit, v.idx, v.idx, c*mass[v]); 64 | } 65 | 66 | Eigen::SparseMatrix A(n,n); 67 | A.setFromTriplets(triplets.begin(), triplets.end()); 68 | 69 | Eigen::CholmodSupernodalLLT> solver(A); 70 | Eigen::Map{vertexData.data(), int(n)} = solver.solve(b); 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /Mesh/SmoothVertexData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Surface.h" 4 | 5 | namespace Phasefield { 6 | 7 | void smoothVertexData(VertexData& vertexData, Mesh& mesh, double c); 8 | 9 | } -------------------------------------------------------------------------------- /Mesh/Surface.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/7/20. 3 | // 4 | 5 | #pragma once 6 | 7 | namespace Phasefield { 8 | 9 | class Mesh; 10 | 11 | struct Vertex; 12 | struct Edge; 13 | struct HalfEdge; 14 | struct Face; 15 | struct Corner; 16 | struct DualEdge; 17 | 18 | template struct ElementIterator; 19 | template struct FaceCirculationIterator; 20 | template struct VertexCirculationIterator; 21 | 22 | using FaceCornerIterator = FaceCirculationIterator; 23 | using FaceVertexIterator = FaceCirculationIterator; 24 | using FaceEdgeIterator = FaceCirculationIterator; 25 | using FaceHalfEdgeIterator = FaceCirculationIterator; 26 | using FaceDualEdgeIterator = FaceCirculationIterator; 27 | 28 | using VertexCornerIterator = VertexCirculationIterator; 29 | using VertexVertexIterator = VertexCirculationIterator; 30 | using VertexEdgeIterator = VertexCirculationIterator; 31 | using VertexFaceIterator = VertexCirculationIterator; 32 | using VertexIncomingHalfEdgeIterator = VertexCirculationIterator; 33 | struct VertexOutgoingHalfEdgeIterator; 34 | 35 | using VertexIterator = ElementIterator; 36 | using FaceIterator = ElementIterator; 37 | using EdgeIterator = ElementIterator; 38 | using HalfEdgeIterator = ElementIterator; 39 | using CornerIterator = ElementIterator; 40 | using DualEdgeIterator = ElementIterator; 41 | 42 | template struct Range; 43 | 44 | using VertexEdgeRange = Range; 45 | using VertexCornerRange = Range; 46 | using VertexVertexRange = Range; 47 | using VertexOutgoingHalfEdgeRange = Range; 48 | using VertexIncomingHalfEdgeRange = Range; 49 | using VertexFaceRange = Range; 50 | 51 | using FaceEdgeRange = Range; 52 | using FaceCornerRange = Range; 53 | using FaceVertexRange = Range; 54 | using FaceHalfEdgeRange = Range; 55 | using FaceDualEdgeRange = Range; 56 | 57 | using VertexSet = Range; 58 | using FaceSet = Range; 59 | using EdgeSet = Range; 60 | using HalfEdgeSet = Range; 61 | using CornerSet = Range; 62 | using DualEdgeSet = Range; 63 | 64 | 65 | template class MeshDataView; 66 | template class MeshData; 67 | 68 | template using VertexData = MeshData; 69 | template using VertexDataView = MeshDataView; 70 | 71 | template using FaceData = MeshData; 72 | template using FaceDataView = MeshDataView; 73 | 74 | template using EdgeData = MeshData; 75 | template using EdgeDataView = MeshDataView; 76 | 77 | template using HalfEdgeData = MeshData; 78 | template using HalfEdgeDataView = MeshDataView; 79 | 80 | template using CornerData = MeshData; 81 | template using CornerDataView = MeshDataView; 82 | 83 | template using DualEdgeData = MeshData; 84 | template using DualEdgeDataView = MeshDataView; 85 | 86 | struct FEM; 87 | } -------------------------------------------------------------------------------- /Optimization/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) 2 | 3 | add_library(Optimization STATIC 4 | Functional.h 5 | Functional.hpp 6 | Functional.cpp 7 | LossFunction.cpp 8 | LossFunctions.h 9 | Solver.cpp 10 | Solver.h 11 | Optimization.h 12 | Tag.h 13 | Tag.cpp 14 | RecursiveProblem.h 15 | RecursiveProblem.cpp 16 | Tree.h 17 | Tree.cpp 18 | LbfgsSolver.cpp 19 | LbfgsSolver.h 20 | ) 21 | 22 | target_include_directories(Optimization PUBLIC 23 | $) 24 | 25 | target_link_libraries(Optimization PRIVATE 26 | LBFGS::lib 27 | ScopedTimer::ScopedTimer 28 | Phasefield::Viewer 29 | ) 30 | 31 | target_link_libraries(Optimization PUBLIC 32 | Phasefield::Mesh 33 | Phasefield::VisualizationProxy 34 | Corrade::Containers 35 | ) 36 | 37 | add_library(Phasefield::Optimization ALIAS Optimization) 38 | 39 | if (PHASEFIELD_WITH_CERES) 40 | target_link_libraries(Optimization PRIVATE Ceres::ceres) 41 | target_compile_definitions(Optimization PUBLIC PHASEFIELD_WITH_CERES) 42 | endif() 43 | 44 | if (PHASEFIELD_WITH_IPOPT) 45 | target_link_libraries(Optimization PRIVATE PkgConfig::Ipopt) 46 | target_compile_definitions(Optimization PUBLIC PHASEFIELD_WITH_IPOPT) 47 | endif() 48 | 49 | if (PHASEFIELD_WITH_ADOLC) 50 | target_link_libraries(Optimization PRIVATE PkgConfig::Adolc) 51 | target_compile_definitions(Optimization PUBLIC PHASEFIELD_WITH_ADOLC) 52 | endif() 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Optimization/Functional.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 20.04.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Enums.h" 8 | #include "Types.h" 9 | #include "LossFunctions.h" 10 | #include "Visualization.h" 11 | 12 | #include 13 | 14 | class adouble; 15 | 16 | namespace Phasefield { 17 | 18 | struct Functional { 19 | 20 | //enum class Traits { 21 | // HasJacobian, 22 | // HasHessian 23 | //}; 24 | 25 | [[nodiscard]] std::size_t numParameters() const; 26 | 27 | void swap(Functional& other); 28 | 29 | explicit Functional() = default; 30 | explicit Functional(std::nullptr_t) {} 31 | 32 | template 33 | /* implicit */ Functional(F f); 34 | 35 | Functional(Functional const&) = delete; 36 | 37 | Functional& operator=(Functional const&) = delete; 38 | 39 | Functional(Functional&& other) noexcept; 40 | 41 | Functional& operator=(Functional&& other) noexcept; 42 | 43 | ~Functional(); 44 | 45 | //void retape(Containers::StridedArrayView1D const&) const; 46 | 47 | void operator()(ArrayView parameters, 48 | ArrayView weights, 49 | double& out, 50 | ArrayView gradP, 51 | ArrayView gradW) const; 52 | 53 | void loadParameters(Cr::Utility::ConfigurationGroup const&); 54 | 55 | void saveParameters(Cr::Utility::ConfigurationGroup&) const; 56 | 57 | void drawImGuiOptions(VisualizationProxy&); 58 | 59 | /* mandatory */ 60 | void* erased = nullptr; 61 | 62 | void (* destroy)(void*); 63 | 64 | size_t (* params)(void*); 65 | 66 | /* optional */ 67 | void (* load)(void*, Cr::Utility::ConfigurationGroup const&) = nullptr; 68 | void (* save)(void*, Cr::Utility::ConfigurationGroup&) = nullptr; 69 | 70 | //void (* off)(void*) = nullptr; 71 | 72 | void (* options)(void*, VisualizationProxy&) = nullptr; 73 | 74 | //std::size_t (* residuals)(void*) = nullptr; 75 | 76 | void (* evalWithGrad)(void*, ArrayView parameters, 77 | ArrayView weights, 78 | double& out, 79 | ArrayView gradP, 80 | ArrayView gradW); 81 | 82 | void (* ad)(void*, ArrayView, ArrayView, adouble&) = nullptr; 83 | 84 | bool check(ArrayView) const; 85 | 86 | //int tag = -1; 87 | //mutable bool isFirstEvaluation = false; 88 | 89 | bool checkDerivatives = false; 90 | FunctionalType::Value functionalType = FunctionalType::Unknown; 91 | 92 | LossFunction loss = TrivialLoss{}; 93 | double* scaling = nullptr; 94 | 95 | size_t tag = Invalid; 96 | bool drawGradient = false; 97 | bool disable = false; 98 | }; 99 | 100 | 101 | #define DECLARE_FUNCTIONAL_OPERATOR(name, type) \ 102 | extern template void name::operator()(ArrayView parameters, \ 103 | ArrayView weights, \ 104 | type& out, \ 105 | ArrayView gradP, \ 106 | ArrayView gradW); 107 | 108 | #define DEFINE_FUNCTIONAL_OPERATOR(name, type) \ 109 | template void name::operator()(ArrayView parameters, \ 110 | ArrayView weights, \ 111 | type& out, \ 112 | ArrayView gradP, \ 113 | ArrayView gradW); 114 | 115 | #define DECLARE_FUNCTIONAL_CONSTRUCTOR(name) extern template Functional::Functional(name); 116 | #define DEFINE_FUNCTIONAL_CONSTRUCTOR(name) template Functional::Functional(name); 117 | 118 | } 119 | 120 | -------------------------------------------------------------------------------- /Optimization/Functional.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 7/1/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Tag.h" 8 | #include "Functional.h" 9 | #include "Allocate.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace Phasefield { 15 | 16 | template 17 | constexpr bool valid = std::is_invocable_r_v; 18 | 19 | template 20 | Functional::Functional(F f): tag(getTag()) { 21 | 22 | auto checkDraw = [](auto&& f) -> decltype(f.drawImGuiOptions(std::declval())) {}; 23 | auto checkSave = [](auto&& f) -> decltype(f.saveParameters(std::declval())) {}; 24 | auto checkLoad = [](auto&& f) -> decltype(f.loadParameters(std::declval())) {}; 25 | auto checkType = [](auto&& f) -> decltype(decltype(f)::type()) {}; 26 | 27 | evalWithGrad = +[]( 28 | void* e, 29 | ArrayView p, 30 | ArrayView w, 31 | double& r, 32 | ArrayView gradP, 33 | ArrayView gradW) { 34 | return (*static_cast(e))(p, w, r, gradP, gradW); 35 | }; 36 | 37 | #ifdef PHASEFIELD_WITH_ADOLC 38 | ad = +[]( 39 | void* e, 40 | ArrayView p, 41 | ArrayView w, 42 | adouble& r) { 43 | return (*static_cast(e)).template operator()(p, w, r, nullptr, nullptr); 44 | }; 45 | #endif 46 | 47 | /* mandatory */ 48 | erased = allocate_buffer(sizeof(F), alignof(F)); 49 | ::new(erased) F(std::move(f)); 50 | 51 | 52 | destroy = +[](void* e) { 53 | static_cast(e)->~F(); 54 | deallocate_buffer(e, sizeof(F), alignof(F)); 55 | }; 56 | 57 | params = +[](void* e) { return static_cast(e)->numParameters(); }; 58 | 59 | 60 | /* optional */ 61 | if constexpr(valid) { 62 | options = +[](void* e, VisualizationProxy& p) { static_cast(e)->drawImGuiOptions(p); }; 63 | } 64 | 65 | if constexpr (valid) { 66 | save = +[](void* e, Cr::Utility::ConfigurationGroup& group) { static_cast(e)->saveParameters(group); }; 67 | } 68 | 69 | if constexpr (valid) { 70 | load = +[](void* e, Cr::Utility::ConfigurationGroup const& group) { static_cast(e)->loadParameters(group); }; 71 | } 72 | 73 | if constexpr (valid) 74 | functionalType = F::type(); 75 | 76 | Debug{} << tag; 77 | } 78 | 79 | 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /Optimization/LbfgsSolver.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 11/1/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Types.h" 8 | #include "RecursiveProblem.h" 9 | #include 10 | 11 | namespace Phasefield { 12 | 13 | /** 14 | * This is dumb wrapper around the lbfgs solver (see contrib module). 15 | * The original solver only allows to call into the solver once 16 | */ 17 | class LbfgsSolver { 18 | public: 19 | 20 | LbfgsSolver(Solver::Options& options, Solver::RecursiveProblem& problem, ArrayView data); 21 | 22 | LbfgsSolver(LbfgsSolver const&) = delete; 23 | LbfgsSolver(LbfgsSolver&&) = delete; 24 | LbfgsSolver& operator=(LbfgsSolver&&) = delete; 25 | LbfgsSolver& operator=(LbfgsSolver const&) = delete; 26 | 27 | ~LbfgsSolver(); 28 | 29 | int runOneIteration(); 30 | 31 | private: 32 | 33 | ArrayView m_x; 34 | double* m_fx; 35 | Solver::RecursiveProblem& m_problem; 36 | Solver::Options& m_options; 37 | 38 | struct Data; 39 | Data* m_data; 40 | 41 | void* m_cd; 42 | 43 | int ret; 44 | int i, j, k, end, bound; 45 | 46 | double step; 47 | double ys, yy; 48 | double xnorm, gnorm, beta; 49 | double fx = 0.; 50 | double rate = 0.; 51 | }; 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /Optimization/LossFunction.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 20.04.20. 3 | 4 | #include "LossFunctions.h" 5 | #include "SmartEnum.h" 6 | #include "Allocate.h" 7 | 8 | #include 9 | #include 10 | 11 | #ifdef PHASEFIELD_WITH_ADOLC 12 | #include 13 | #endif 14 | 15 | #include 16 | #include 17 | 18 | namespace Phasefield { 19 | 20 | template 21 | constexpr bool valid = std::is_invocable_r_v; 22 | 23 | template 24 | LossFunction::LossFunction(T f) { 25 | 26 | auto checkDraw = [](auto&& f) -> decltype(f.drawSettings()){}; 27 | auto checkType = [](auto&& f) -> decltype(decltype(f)::type()) {}; 28 | 29 | erased = allocate_buffer(sizeof(T), alignof(T)); 30 | ::new(erased) T(std::move(f)); 31 | 32 | #ifdef PHASEFIELD_WITH_ADOLC 33 | ad = +[](void* e, adouble const& x, adouble& y) { 34 | (*static_cast(e))(x, y); 35 | }; 36 | #endif 37 | 38 | loss = +[](void* e, double x, double ys[3]) { 39 | (*static_cast(e))(x, ys); 40 | }; 41 | destroy = +[](void* e) { 42 | static_cast(e)->~T(); 43 | deallocate_buffer(e, sizeof(T), alignof(T)); 44 | }; 45 | 46 | if constexpr (valid) 47 | lossType = T::type(); 48 | else 49 | lossType = LossFunctionType::Unknown; 50 | 51 | /* optional */ 52 | //constexpr bool hasSettings = is_valid(f)([](auto&& p) constexpr -> decltype(f.drawSettings()){}); 53 | if constexpr(valid) { 54 | draw = +[](void* e) { static_cast(e)->drawSettings(); }; 55 | } 56 | } 57 | 58 | LossFunction::LossFunction(LossFunctionType::Value t) { 59 | switch(t) { 60 | case LossFunctionType::Cauchy : *this = CauchyLoss{}; break; 61 | case LossFunctionType::Trivial : *this = TrivialLoss{}; break; 62 | case LossFunctionType::Quadratic : *this = QuadraticLoss{}; break; 63 | default : CORRADE_ASSERT(false, "Unknown loss type",); 64 | } 65 | } 66 | 67 | void LossFunction::drawSettings() { 68 | if(ImGui::BeginCombo("##loss", LossFunctionType::to_string(lossType))) { 69 | for(auto l : LossFunctionType::range) { 70 | if(l == LossFunctionType::Unknown) continue; 71 | bool isSelected = l == lossType; 72 | if(ImGui::Selectable(LossFunctionType::to_string(l), isSelected)) 73 | *this = LossFunction(l); 74 | if(isSelected) { 75 | ImGui::SetItemDefaultFocus(); 76 | } 77 | } 78 | 79 | ImGui::EndCombo(); 80 | } 81 | 82 | if(draw) { 83 | draw(erased); 84 | } 85 | ImGui::InputDouble("Weight", &weight); 86 | } 87 | 88 | LossFunction::~LossFunction() { 89 | if(erased) { 90 | destroy(erased); 91 | } 92 | } 93 | 94 | LossFunction::LossFunction(LossFunction&& other) noexcept { 95 | swap(other); 96 | } 97 | 98 | LossFunction& LossFunction::operator=(LossFunction&& other) noexcept { 99 | swap(other); 100 | return *this; 101 | } 102 | 103 | void LossFunction::swap(LossFunction& other) { 104 | std::swap(ad, other.ad); 105 | std::swap(loss, other.loss); 106 | std::swap(destroy, other.destroy); 107 | std::swap(erased, other.erased); 108 | std::swap(lossType, other.lossType); 109 | std::swap(weight, other.weight); 110 | std::swap(draw, other.draw); 111 | } 112 | 113 | void LossFunction::operator()(double const& in, double out[3]) const { 114 | loss(erased, in, out); 115 | for(size_t i = 0; i < 3; ++i) out[i] *= weight; 116 | } 117 | 118 | #ifdef PHASEFIELD_WITH_ADOLC 119 | void LossFunction::operator()(adouble const& x, adouble& y) const { 120 | ad(erased, x, y); 121 | y *= weight; 122 | } 123 | #endif 124 | 125 | void TrivialLoss::operator()(double const& in, double out[3]) const { 126 | out[0] = in; 127 | out[1] = 1.; 128 | out[2] = 0.; 129 | } 130 | 131 | void QuadraticLoss::operator()(double const& in, double out[3]) const { 132 | out[0] = in*in; 133 | out[1] = 2*in; 134 | out[2] = 2.; 135 | } 136 | 137 | void CauchyLoss::operator()(double const& in, double out[3]) const { 138 | double sum = in + 1.; 139 | double inv = 1./sum; 140 | out[0] = log(sum); 141 | out[1] = inv; 142 | out[2] = -1.*inv*inv; 143 | } 144 | 145 | 146 | /* explicit instantiations */ 147 | template LossFunction::LossFunction(TrivialLoss); 148 | 149 | template LossFunction::LossFunction(CauchyLoss); 150 | 151 | template LossFunction::LossFunction(QuadraticLoss); 152 | 153 | } -------------------------------------------------------------------------------- /Optimization/LossFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SmartEnum.h" 4 | 5 | class adouble; 6 | 7 | namespace Phasefield { 8 | 9 | SMART_ENUM(LossFunctionType, int, 10 | Trivial, 11 | Cauchy, 12 | Quadratic, 13 | Unknown) 14 | 15 | /* type erasing wrapper around a loss function */ 16 | struct LossFunction { 17 | 18 | template 19 | LossFunction(T f); 20 | 21 | LossFunction(LossFunctionType::Value); 22 | 23 | ~LossFunction(); 24 | 25 | LossFunction(LossFunction const&) = delete; 26 | 27 | LossFunction& operator=(LossFunction const&) = delete; 28 | 29 | LossFunction(LossFunction&& other) noexcept; 30 | 31 | LossFunction& operator=(LossFunction&& other) noexcept; 32 | 33 | void swap(LossFunction& other); 34 | 35 | void operator()(double const& in, double out[3]) const; 36 | 37 | #ifdef PHASEFIELD_WITH_ADOLC 38 | void operator()(adouble const& x, adouble& y) const; 39 | #endif 40 | 41 | void drawSettings(); 42 | 43 | void (* ad)(void*, adouble const&, adouble&) = nullptr; 44 | 45 | void (* loss)(void*, double, double[3]) = nullptr; 46 | 47 | void (* draw)(void*) = nullptr; 48 | 49 | void (* destroy)(void*) = nullptr; 50 | 51 | void* erased = nullptr; 52 | 53 | LossFunctionType::Value lossType = LossFunctionType::Unknown; 54 | double weight = 1.; 55 | }; 56 | 57 | void drawLossFunction(LossFunction&); 58 | 59 | struct TrivialLoss { 60 | 61 | void operator()(double const& in, double out[3]) const; 62 | 63 | static LossFunctionType::Value type() { return LossFunctionType::Trivial; } 64 | 65 | template 66 | void operator()(T const& x, T& y) const { 67 | y = x; 68 | } 69 | }; 70 | 71 | struct QuadraticLoss { 72 | 73 | void operator()(double const& in, double out[3]) const; 74 | 75 | template 76 | void operator()(T const& x, T& y) const { 77 | y = x*x; 78 | } 79 | 80 | static LossFunctionType::Value type() { return LossFunctionType::Quadratic; } 81 | }; 82 | 83 | struct CauchyLoss { 84 | 85 | void operator()(double const& in, double out[3]) const; 86 | 87 | template 88 | void operator()(T const& x, T& y) const { 89 | y = log(1 + x); 90 | } 91 | 92 | static LossFunctionType::Value type() { return LossFunctionType::Cauchy; } 93 | }; 94 | 95 | /* explicit template declarations */ 96 | extern template LossFunction::LossFunction(TrivialLoss); 97 | 98 | extern template LossFunction::LossFunction(CauchyLoss); 99 | 100 | extern template LossFunction::LossFunction(QuadraticLoss); 101 | 102 | } -------------------------------------------------------------------------------- /Optimization/Optimization.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 7/1/20. 3 | // 4 | 5 | #pragma once 6 | 7 | //forward declarations 8 | 9 | namespace Phasefield { 10 | 11 | struct Functional; 12 | struct LossFunction; 13 | struct SparseMatrix; 14 | 15 | struct Tree; 16 | struct Node; 17 | 18 | template struct NodeIterator; 19 | using LeafIterator = NodeIterator<0>; 20 | using InternalNodeIterator = NodeIterator<1>; 21 | using HorizontalNodeIterator = NodeIterator<2>; 22 | 23 | namespace Solver { 24 | 25 | struct Problem; 26 | struct RecursiveProblem; 27 | 28 | struct IterationSummary; 29 | 30 | struct Options; 31 | struct Summary; 32 | 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /Optimization/Problem.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 28.03.20. 3 | // 4 | 5 | #pragma once 6 | 7 | 8 | #include "VisualizationProxy.h" 9 | #include "Functional.h" 10 | #include "Optimization.h" 11 | #include "SparseMatrix.h" 12 | 13 | #include 14 | 15 | namespace Phasefield::Solver { 16 | 17 | struct Problem { 18 | 19 | explicit Problem(); 20 | 21 | ~Problem(); 22 | 23 | [[nodiscard]] std::size_t numParameters() const; 24 | 25 | [[nodiscard]] std::size_t numConstraints() const; 26 | 27 | Containers::Array objectives, constraints; 28 | SparseMatrix hessian, jacobian; /* hessian of the lagrangian, jacobian of the constraints */ 29 | VisualizationProxy* proxy = nullptr; /* pipe to visualize stuff */ 30 | int tagL, tagJ, tagG; 31 | 32 | void updateInternalDataStructures(const Containers::Array&); 33 | 34 | void evaluate( 35 | double const* parameters, 36 | double* residual, 37 | double* constr, 38 | double* g, 39 | SparseMatrix* j, 40 | SparseMatrix* h, 41 | double objectiveScale, 42 | double const* lambdas) const; 43 | }; 44 | 45 | } -------------------------------------------------------------------------------- /Optimization/RecursiveProblem.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 7/16/20. 3 | // 4 | 5 | #include "RecursiveProblem.h" 6 | #include "SparseMatrix.h" 7 | #include "C1Functions.h" 8 | #include "Types.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace Phasefield::Solver { 14 | 15 | 16 | RecursiveProblem::RecursiveProblem(Tree& t) : tree(t) {} 17 | 18 | size_t RecursiveProblem::numParameters() const { return tree.vertexCount(); } 19 | 20 | void RecursiveProblem::operator()( 21 | Containers::ArrayView data, 22 | double& cost, 23 | Containers::ArrayView gradData, 24 | Containers::ArrayView constr, 25 | SparseMatrix* jacobian) const { 26 | 27 | //SmootherStep smoothStep; 28 | 29 | //auto& nodes = tree.nodes; 30 | //auto n = tree.vertexCount(); 31 | //auto m = numConstraints(); 32 | //auto size = nodes.size(); 33 | 34 | ////auto gradients = tree.gradients(); 35 | //auto& jacValues = jacobian->values; 36 | //auto prefixes = tree.temporaryData(); 37 | //StridedArrayView2D phasefields{data, {size, n}}; 38 | //StridedArrayView2D gradients{gradData, {size, n}}; 39 | 40 | //cost = 0.; 41 | //for(auto& x : prefixes[0]) x = 1.; 42 | 43 | //int jacIdx = 0; 44 | //tree.traverse([&](Node& node) -> bool { 45 | // if(!node.isLeaf()){ 46 | // for(UnsignedInt i = 0; i < n; ++i){ 47 | // if(node.m_leftChild != Invalid){ 48 | // double pos = smoothStep.eval(phasefields[node.m_leftChild][i]); 49 | // prefixes[node.m_leftChild][i] = pos*prefixes[node.idx][i]; 50 | // } 51 | // if(node.m_rightChild != Invalid){ 52 | // double neg = smoothStep.eval(-phasefields[node.m_leftChild][i]); 53 | // prefixes[node.m_rightChild][i] = neg*prefixes[node.idx][i]; 54 | // } 55 | // } 56 | // } else { 57 | // Node const& leaf = node; 58 | 59 | // Array productsGrad(Containers::ValueInit, n); 60 | // Array postfix(Containers::NoInit, n); 61 | // Array productsJacData(Containers::ValueInit, n*m); 62 | // Containers::StridedArrayView2D productsJac(productsJacData, {m, n}); 63 | 64 | // for(Functional const& functional : objectives){ 65 | // functional(phasefields[node.idx].asContiguous(), 66 | // prefixes[node.idx].asContiguous(), cost, 67 | // gradients[leaf.idx].asContiguous(), productsGrad); 68 | // } 69 | 70 | // for(std::size_t i = 0; i < constraints.size(); ++i){ 71 | // auto jacSlice = jacValues.slice(jacIdx, jacIdx + n); 72 | // constraints[i](phasefields[node.idx].asContiguous(), 73 | // prefixes[node.idx].asContiguous(), cost, 74 | // jacSlice, productsJac[i].asContiguous()); 75 | // jacIdx += n; 76 | // } 77 | 78 | // for(double& x : postfix) x = 1.; 79 | 80 | // Node const* node1 = &leaf; 81 | // while(true) { 82 | // size_t idx = node1->idx; 83 | // double sign = tree.isLeftChild(*node1); 84 | // for(UnsignedInt i = 0; i < n; ++i){ 85 | // gradients[idx][i] += prefixes[idx][i]*smoothStep.grad(sign*phasefields[idx][i])*postfix[i]*productsGrad[i]; 86 | // postfix[i] *= phasefields[idx][i]; 87 | // } 88 | 89 | // for(std::size_t j = 0; j < m; ++j) { 90 | // for(UnsignedInt i = 0; i < n; ++i) { 91 | // jacValues[jacIdx++] += prefixes[idx][i]*smoothStep.grad(sign*phasefields[idx][i])*postfix[i]*productsJac[j][i]; 92 | // } 93 | // } 94 | 95 | // if(node1->m_parent != Invalid){ 96 | // break; 97 | // } else { 98 | // node1 = &tree.nodes[node1->m_parent]; 99 | // } 100 | // } 101 | // } 102 | // return true; 103 | //}); 104 | } 105 | 106 | void RecursiveProblem::operator()(ArrayView parameters, double& cost, ArrayView gradient) const { 107 | size_t n = tree.vertexCount(); 108 | 109 | if(gradient) 110 | for(double& x : gradient) x = 0.; 111 | cost = 0.; 112 | 113 | if(evaluateObjective) { 114 | for(auto const& [f, hist, show] : objectives) { 115 | if(!f.disable) { 116 | f(parameters, nodeToOptimize.temporary(), cost, gradient, nullptr); 117 | } 118 | } 119 | } else { 120 | for(Functional const& f : constraints) { 121 | if(!f.disable) { 122 | f(parameters, nodeToOptimize.temporary(), cost, gradient, nullptr); 123 | } 124 | } 125 | } 126 | 127 | } 128 | 129 | 130 | } 131 | -------------------------------------------------------------------------------- /Optimization/RecursiveProblem.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 7/16/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Functional.h" 8 | #include "Tree.h" 9 | 10 | #include 11 | 12 | namespace Phasefield::Solver { 13 | 14 | struct RecursiveProblem { 15 | 16 | explicit RecursiveProblem(Tree& t); 17 | 18 | [[nodiscard]] size_t numParameters() const; 19 | 20 | //[[nodiscard]] size_t numConstraints() const; 21 | 22 | void operator()( 23 | Containers::ArrayView parameters, 24 | double& cost, 25 | Containers::ArrayView gradient, 26 | Containers::ArrayView constraints, 27 | SparseMatrix* jacobian) const; 28 | 29 | void operator()( 30 | Containers::ArrayView parameters, 31 | double& cost, 32 | Containers::ArrayView gradient) const; 33 | 34 | //void determineSparsityStructure(SparseMatrix& jacobian) const; 35 | 36 | Tree& tree; 37 | Node nodeToOptimize{Invalid, nullptr}; 38 | 39 | struct Objective { 40 | Functional f{nullptr}; 41 | Array history; 42 | bool show = true; 43 | }; 44 | 45 | bool evaluateObjective = true; 46 | 47 | Array objectives; 48 | Array constraints; 49 | }; 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Optimization/Solver.h: -------------------------------------------------------------------------------- 1 | // int reated by janos on 20.04.20. 2 | // 3 | 4 | #pragma once 5 | 6 | #include "SmartEnum.h" 7 | #include "UniqueFunction.h" 8 | #include "Optimization.h" 9 | #include "Types.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace Cr = Corrade; 15 | namespace Mg = Magnum; 16 | 17 | namespace Phasefield::Solver { 18 | 19 | SMART_ENUM(Backend, size_t, 20 | IPOPT, 21 | CERES, 22 | LBFGSLIB) 23 | 24 | SMART_ENUM(Status, size_t, 25 | CONTINUE, 26 | USER_ABORTED, 27 | FINISHED) 28 | 29 | SMART_ENUM(LineSearchDirection, size_t, 30 | STEEPEST_DESCENT, 31 | NONLINEAR_CONJUGATE_GRADIENT, 32 | LBFGS, 33 | BFGS, 34 | SR1) 35 | 36 | struct IterationSummary { 37 | }; 38 | 39 | using IterationCallbackType = UniqueFunction; 40 | 41 | struct Options { 42 | size_t max_num_iterations = 100; 43 | bool minimizer_progress_to_stdout = true; 44 | bool update_state_every_iteration = true; 45 | LineSearchDirection::Value line_search_direction = LineSearchDirection::LBFGS; 46 | Backend::Value solver = Backend::LBFGSLIB; 47 | Array callbacks; 48 | }; 49 | 50 | struct Summary { 51 | 52 | }; 53 | 54 | //void solve(Options& options, Problem& problem, double*, Summary* summary = nullptr); 55 | 56 | void solve(Options& options, RecursiveProblem& problem, ArrayView data, Summary* summary = nullptr); 57 | 58 | } 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Optimization/Tag.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 7/14/20. 3 | // 4 | 5 | #include "Tag.h" 6 | #include "Types.h" 7 | #include "StlAlgorithm.h" 8 | 9 | #include 10 | 11 | namespace Phasefield { 12 | 13 | static Array tags; 14 | 15 | size_t getTag() { 16 | size_t tag = Invalid; 17 | for(size_t i = 1; i < tags.size(); ++i) { 18 | if(tags[i] > tags[i - 1] + 1) { 19 | tag = tags[i - 1] + 1; 20 | break; 21 | } 22 | } 23 | 24 | if(tag == Invalid) 25 | tag = tags.empty() ? 0 : tags.back() + 1; 26 | 27 | 28 | arrayAppend(tags, tag); 29 | std::sort(tags.begin(), tags.end()); 30 | return tag; 31 | } 32 | 33 | bool deleteTag(size_t tag) { 34 | auto it = std::lower_bound(tags.begin(), tags.end(), tag); 35 | if(it < tags.end() - 1) {/* if the tag is not in the last position memmove everything back by one */ 36 | std::memmove(it, it + 1, sizeof(size_t)*(tags.end() - (it + 1))); 37 | arrayResize(tags, tags.size() - 1); 38 | } else if(it != tags.end()) { /* if the tag is in the last position just pop the back */ 39 | arrayResize(tags, tags.size() - 1); 40 | } else return false; 41 | return true; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Optimization/Tag.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 7/14/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace Phasefield { 10 | 11 | size_t getTag(); 12 | 13 | bool deleteTag(size_t tag); 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Optimization/Tree.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 08.06.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Enums.h" 8 | #include "Range.h" 9 | #include "Types.h" 10 | #include "Mesh.h" 11 | #include "Serialize.h" 12 | #include "Optimization.h" 13 | 14 | #include 15 | #include 16 | 17 | namespace Phasefield { 18 | 19 | namespace Cr = Corrade; 20 | 21 | struct NodeData { 22 | size_t leftChild = Invalid; 23 | size_t rightChild = Invalid; 24 | size_t parent = Invalid; 25 | size_t depth = 0; 26 | }; 27 | 28 | SMART_ENUM(Initialization, size_t, RANDOM, NORMAL_CLUSTER, BFS) 29 | 30 | struct Node { 31 | size_t idx = Invalid; 32 | Tree* tree = nullptr; 33 | 34 | bool operator != (Node const& other) const { return idx != other.idx; } 35 | bool operator == (Node const& other) const { return idx == other.idx; } 36 | bool operator < (Node const& other) const { return idx < other.idx; } 37 | bool operator > (Node const& other) const { return idx > other.idx; } 38 | 39 | [[nodiscard]] bool isLeaf() const { return !hasLeftChild() && !hasRightChild(); } 40 | 41 | [[nodiscard]] bool isValid() const { return idx != Invalid; } 42 | 43 | [[nodiscard]] bool hasLeftChild() const { return leftChild().isValid(); } 44 | 45 | [[nodiscard]] bool hasRightChild() const { return rightChild().isValid(); } 46 | 47 | [[nodiscard]] bool isLeftChild() const { return parent().leftChild() == *this; } 48 | 49 | [[nodiscard]] bool isRightChild() const { return parent().rightChild() == *this; } 50 | 51 | void splitAndInitializeChildren(Node* n); 52 | 53 | [[nodiscard]] double integrateWeight(Mesh& mesh) const; 54 | 55 | [[nodiscard]] VertexDataView phasefield() const; 56 | 57 | [[nodiscard]] VertexDataView temporary() const; 58 | 59 | [[nodiscard]] Node parent() const; 60 | 61 | Node addLeftChild(Node* n = nullptr); 62 | 63 | Node addRightChild(Node* n = nullptr); 64 | 65 | Node addChild(bool left, Node* n); 66 | 67 | void initialize(Initialization::Value = Initialization::NORMAL_CLUSTER) const; 68 | 69 | [[nodiscard]] size_t depth() const; 70 | 71 | [[nodiscard]] Node leftChild() const; 72 | 73 | [[nodiscard]] Node rightChild() const; 74 | 75 | explicit operator bool() const { return idx != Invalid; } 76 | }; 77 | 78 | Debug& operator<<(Debug& debug, Node const& n); 79 | 80 | struct Tree { 81 | 82 | friend Node; 83 | 84 | explicit Tree(Mesh&); 85 | 86 | Mesh* mesh = nullptr; 87 | 88 | Array phasefieldData; 89 | Array tempsData; 90 | 91 | Array nodeData; 92 | 93 | size_t numLeafs = 0; 94 | size_t depth = 0; 95 | 96 | Node root() { return {0, this}; } 97 | 98 | ArrayView level(size_t d); 99 | 100 | void update(); 101 | 102 | /** 103 | * If the level does not exist, the number of nodes is returned. 104 | * @param level depth in the tree 105 | * @return idx of the first node on the requested level 106 | */ 107 | size_t levelStartIndex(size_t level); 108 | 109 | size_t nodeCountOnLevel(size_t level) { return levelStartIndex(level + 1) - levelStartIndex(level); } 110 | 111 | //void subdivide(Containers::Array& indices, Containers::Array& vertices); 112 | 113 | //void remove(Node node); 114 | StridedArrayView2D phasefields(); 115 | 116 | Node insertNodeAtIndex(size_t idx); 117 | 118 | void reset(); 119 | 120 | Range nodesOnLevel(size_t l); 121 | 122 | Range nodesBelowLevel(size_t l); 123 | 124 | Range nodes(); 125 | 126 | Range internalNodes(); 127 | 128 | Array ancestorsOfLevel(size_t l); 129 | 130 | Range leafs(); 131 | 132 | [[nodiscard]] size_t nodeCount() const { return nodeData.size(); } 133 | 134 | [[nodiscard]] size_t vertexCount() const { return phasefieldData.size()/nodeCount(); } 135 | 136 | void computeWeightsOfAncestorsOfLevel(size_t l); 137 | 138 | void computeLeafWeights(); 139 | 140 | void serialize(Array& data) const; 141 | 142 | static Tree deserialize(ArrayView const& data, Mesh& m); 143 | 144 | void computeAdjacencyGraph(Array& neighbors, Array& starts, size_t level); 145 | }; 146 | 147 | template 148 | struct NodeIterator { 149 | Node node; 150 | 151 | Node operator *() const { return node; } 152 | 153 | NodeIterator& operator++() { 154 | if constexpr (IteratorType == 2) { node.idx++; return *this; } 155 | do { node.idx++; } while(node.idx < node.tree->nodeCount() && !node.isLeaf()); 156 | return *this; 157 | } 158 | 159 | bool operator !=(NodeIterator const& other) const { return node != other.node; } 160 | }; 161 | 162 | 163 | } -------------------------------------------------------------------------------- /Phasefield.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Phasefields 6 | 7 | 8 | 9 | 10 |

Variational Surface Segmentation using a Phasefield Discretization

11 |
12 |
13 | 14 |
Initialization...
15 |
16 | 17 | 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Phasefields 2 | ===== 3 | [![Build Status](https://github.com/Janos95/Phasefields/workflows/Build/badge.svg)](https://github.com/Janos95/Phasefields/actions?query=workflow%3ABuild) 4 | 5 | 6 | This repository contains the numerical code for my master thesis. 7 | It provides a GUI application for the hierarchical optimization of phasefields. 8 | Some of the highlights are : 9 | 10 | - A phasefield parameterization of the Yamabe energy, 11 | which can be used to compute surface segmentations which can be conformally mapped 12 | to the plane with low area distortion. 13 | 14 | - A topological penalty term which enforces connectedness of the segments. 15 | 16 | - A hierarchical segmentation scheme, which combines multiple phasefields, to 17 | compute segmentations with more than two segments. 18 | 19 | The GUI also support some other niceties like editing phasefields 20 | using a brush-like tool, mesh IO via Assimp, making and exporting videos 21 | using ffmpeg, color optimization for segmentations etc. 22 | 23 | You can try it out in your [browser](https://janos95.gitlab.io/wasm-test/), but be aware the 24 | webassembly build might have one or two bugs ;). There are four compiled in resources 25 | which can be loaded by choosing the corresponding resource at 'IO/Experiment' and then 26 | pressing the 'Load Experiment' button. Then just need to press the '/Optimization Options/Optimize' 27 | button and the phasefield should start moving. 28 | 29 | Here is a teaser image of the optimization of the Yamabe energy for varying interface length penalties 30 | ![](images/image.png) 31 | 32 | Building 33 | -------- 34 | You will need a C++20 conformant compiler and CMake for 35 | project file generation. 36 | 37 | Before building make sure that git submodules are downloaded. 38 | You can download them via: 39 | 40 | ```bash 41 | git submodules update --init --recursive 42 | ``` 43 | 44 | Build files are generated using cmake. 45 | If you want to build the project on a unix 46 | like platform you can use the following command 47 | 48 | ```bash 49 | mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make -j 50 | ``` 51 | 52 | There are some more dependencies which you can enable using the following cmake options: 53 | - Loading meshes using Assimp (PHASEFIELD_WITH_IO). 54 | - Exporting videos via ffmpeg (PHASEFIELD_WITH_IO) 55 | - Solving constrained optimization problems using Ipopt (PHASEFIELD_WITH_IPOPT). 56 | - Checking derivatives via AD using ADOL-C (PHASEFIELD_WITH_ADOLC). 57 | - Solving the Yamabe equation faster using Suite Sparse (PHASEFIELD_WITH_SUITESPARSE). 58 | - A different lBFGS implementation provided by ceres-solver (PHASEFIELD_WITH_CERES). 59 | - Threading using TBB (PHASEFIELD_WITH_TBB). 60 | 61 | Related Work 62 | -------- 63 | 64 | The diffuse Yamabe energy draws inspiration from the paper 65 | 'Variational Surface Cutting' for which there is also code 66 | available [here](https://github.com/nmwsharp/variational-surface-cutting) 67 | 68 | 69 | -------------------------------------------------------------------------------- /ScopedTimer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(ScopedTimer STATIC include/ScopedTimer/ScopedTimer.cpp) 3 | 4 | target_include_directories(ScopedTimer PUBLIC 5 | $) 6 | 7 | target_link_libraries(ScopedTimer PUBLIC Corrade::Containers) 8 | set_property(TARGET ScopedTimer PROPERTY POSITION_INDEPENDENT_CODE ON) 9 | add_library(ScopedTimer::ScopedTimer ALIAS ScopedTimer) -------------------------------------------------------------------------------- /ScopedTimer/include/ScopedTimer/ScopedTimer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 19.10.19. 3 | // 4 | 5 | #include "ScopedTimer.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using my_clock = std::chrono::steady_clock; 14 | using my_duration = std::chrono::duration; 15 | using time_point_t = std::chrono::time_point; 16 | 17 | template 18 | char const* toSI() 19 | { 20 | if constexpr(std::is_same_v) 21 | return "nano seconds"; 22 | if constexpr(std::is_same_v) 23 | return "micro seconds"; 24 | if constexpr(std::is_same_v) 25 | return "milli seconds"; 26 | if constexpr(std::is_same_v, Ratio>) 27 | return "seconds"; 28 | if constexpr(std::is_same_v, Ratio>) 29 | return "minutes"; 30 | if constexpr(std::is_same_v, Ratio>) 31 | return "hours"; 32 | } 33 | 34 | using Ratio = std::ratio<1>; 35 | using user_dur = std::chrono::duration; 36 | 37 | struct TimingInfo 38 | { 39 | my_duration mean{0}; 40 | double M2 = 0; 41 | std::size_t count = 0; 42 | }; 43 | 44 | struct ScopedTimer::Impl{ 45 | std::string name_; 46 | time_point_t start_; 47 | bool verbose_; 48 | }; 49 | 50 | std::unordered_map log_; 51 | std::mutex mutex_; 52 | 53 | ScopedTimer::ScopedTimer(char const* name, bool verbose): 54 | m_impl(new Impl{name, my_clock::now(), verbose}) 55 | { 56 | } 57 | 58 | ScopedTimer::~ScopedTimer() { 59 | const auto end = my_clock::now(); 60 | my_duration time = end - m_impl->start_; 61 | if(m_impl->verbose_) 62 | printf("%s took %f %s\n", m_impl->name_.c_str(), user_dur{time}.count(), toSI()); 63 | 64 | std::lock_guard l(mutex_); 65 | 66 | auto& [mean, M2, count] = log_[m_impl->name_]; 67 | ++count; 68 | auto delta1 = time - mean; 69 | mean += delta1 / count; 70 | auto delta2 = time - mean; 71 | M2 += delta1.count() * delta2.count(); 72 | 73 | delete m_impl; 74 | } 75 | 76 | void ScopedTimer::printStatistics() 77 | { 78 | std::lock_guard l(mutex_); 79 | for(const auto& [name, timingInfo]: log_) 80 | { 81 | const auto& [mean, M2, count] = timingInfo; 82 | user_dur standardDeviation = my_duration{std::sqrt(M2/static_cast(count - 1))}; 83 | user_dur meanUser = mean; 84 | //auto unit = toSI(); 85 | const char* unit = "seconds"; 86 | printf("%s: Mean %f %s, Standard Deviation %f %s\n", name.c_str(), meanUser.count(), unit, standardDeviation.count(), unit); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /ScopedTimer/include/ScopedTimer/ScopedTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Janos Meny on 9/4/19. 3 | // 4 | 5 | #pragma once 6 | 7 | class ScopedTimer 8 | { 9 | public: 10 | 11 | explicit ScopedTimer(char const* name, bool verbose = false); 12 | 13 | ~ScopedTimer(); 14 | 15 | static void printStatistics(); 16 | 17 | private: 18 | struct Impl; 19 | Impl* m_impl; 20 | }; 21 | -------------------------------------------------------------------------------- /Utilities/Algorithms.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 11/2/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Traits.h" 8 | 9 | namespace Phasefield { 10 | 11 | template 12 | constexpr InputIt find(InputIt first, InputIt last, const T& value) { 13 | for(; first != last; ++first) { 14 | if(*first == value) { 15 | return first; 16 | } 17 | } 18 | return last; 19 | } 20 | 21 | template 22 | constexpr InputIt findIf(InputIt first, InputIt last, UnaryPredicate p) { 23 | for(; first != last; ++first) { 24 | if(p(*first)) { 25 | return first; 26 | } 27 | } 28 | return last; 29 | } 30 | 31 | template 32 | ForwardIt remove(ForwardIt first, ForwardIt last, const T& value) { 33 | first = find(first, last, value); 34 | if(first != last) 35 | for(ForwardIt i = first; ++i != last;) 36 | if(!(*i == value)) 37 | *first++ = MOVE(*i); 38 | return first; 39 | } 40 | 41 | template 42 | ForwardIt removeIf(ForwardIt first, ForwardIt last, UnaryPredicate p) { 43 | first = findIf(first, last, p); 44 | if(first != last) 45 | for(ForwardIt i = first; ++i != last;) 46 | if(!p(*i)) 47 | *first++ = MOVE(*i); 48 | return first; 49 | } 50 | 51 | template 52 | auto removeIf(Rng&& rng, UnaryPredicate p) { 53 | return removeIf(rng.begin(), rng.end(), p); 54 | } 55 | 56 | template 57 | auto removeIf(Rng&& rng, const T& value) { 58 | return removeIf(rng.begin(), rng.end(), value); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /Utilities/Allocate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 10/10/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace Phasefield { 10 | 11 | inline void* allocate_buffer(size_t Size, size_t Alignment) { 12 | return ::operator new(Size 13 | #ifdef __cpp_aligned_new 14 | , 15 | std::align_val_t(Alignment) 16 | #endif 17 | ); 18 | } 19 | 20 | 21 | inline void deallocate_buffer(void* Ptr, size_t Size, size_t Alignment) { 22 | ::operator delete(Ptr 23 | #ifdef __cpp_sized_deallocation 24 | , 25 | Size 26 | #endif 27 | #ifdef __cpp_aligned_new 28 | , 29 | std::align_val_t(Alignment) 30 | #endif 31 | ); 32 | } 33 | 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Utilities/C1Functions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 28.04.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Types.h" 8 | 9 | #include 10 | #include 11 | 12 | struct F { 13 | 14 | template 15 | T eval(const T x) const { 16 | if(x < a) return pow(x - a, 2); 17 | if(x < b) return T{0}; 18 | return pow(x - b, 2); 19 | } 20 | 21 | template 22 | T grad(const T x) const { 23 | if(x < a) return 2*(x - a); 24 | if(x < b) return T{0}; 25 | return 2*(x - b); 26 | } 27 | 28 | double a, b; 29 | }; 30 | 31 | struct ParametricSmoothStep { 32 | double edge0, edge1; 33 | 34 | template 35 | Scalar clamp(Scalar x) { 36 | if (x < 0) 37 | x = 0; 38 | if (x > 1) 39 | x = 1; 40 | return x; 41 | } 42 | 43 | template 44 | Scalar eval(Scalar x) { 45 | Scalar xScaled = (x - edge0) / (edge1 - edge0); 46 | x = clamp(xScaled); 47 | return x * x * (3 - 2 * x); 48 | } 49 | 50 | template 51 | Scalar grad(Scalar x) { 52 | Scalar scale = 1./(edge1 - edge0); 53 | Scalar xScaled = (x - edge0)*scale; 54 | x = clamp(xScaled); 55 | return -6*(-1 + x)*x*scale; 56 | } 57 | }; 58 | 59 | class W { 60 | public: 61 | 62 | W(const double a, const double b) : m_a(a), m_b(b) { 63 | //double steepestPoint = 1./6.*(Math::sqrt(3) * Math::abs(b-a) + 3*(a+b)); 64 | //m_scale = .01/Math::abs(grad(steepestPoint)); 65 | m_scale = -30./pow(a - b, 5); 66 | } 67 | 68 | template 69 | T eval(const T& x) const { 70 | if(x < m_a) 71 | return 0.; 72 | if(x < m_b) 73 | return pow(x - m_a, 2)*pow(x - m_b, 2)*m_scale; 74 | 75 | return 0.; 76 | } 77 | 78 | template 79 | inline Scalar grad(Scalar x) const { 80 | if(x < m_a) 81 | return 0.; 82 | if(x < m_b) 83 | return 2.*m_scale*((x - m_a)*pow(x - m_b, 2) + pow(x - m_a, 2)*(x - m_b)); 84 | return 0.; 85 | } 86 | 87 | private: 88 | const double m_a, m_b; 89 | double m_scale = 1.; 90 | }; 91 | 92 | struct SmootherStep { 93 | template 94 | constexpr T eval(T x) const { 95 | if(x <= -1) return T{0}; 96 | if(x <= 1.){ 97 | x = .5*(x + 1.); 98 | return x*x*(3. - 2.*x); 99 | } 100 | return T{1}; 101 | } 102 | 103 | template 104 | constexpr T grad(T x) const { 105 | if(x <= -1) return T{0}; 106 | if(x <= 1.){ 107 | x = .5*(x + 1.); 108 | return 3.*x*(1. - x); 109 | } 110 | return T{0}; 111 | } 112 | }; 113 | 114 | struct LinearChi { 115 | template 116 | constexpr T eval(T x) const { 117 | return 0.5*(x+1); 118 | } 119 | 120 | template 121 | constexpr T grad(T x) const { 122 | return 0.5; 123 | } 124 | }; 125 | 126 | struct QuadraticChi { 127 | template 128 | constexpr T eval(T x) const { 129 | return 0.25*(x+1)*(x+1); 130 | } 131 | 132 | template 133 | constexpr T grad(T x) const { 134 | return 0.5*(x + 1); 135 | } 136 | }; 137 | 138 | struct QuadraticChiMirrored { 139 | template 140 | constexpr T eval(T x) const { 141 | return 0.25*(x-1)*(x-1); 142 | } 143 | 144 | template 145 | constexpr T grad(T x) const { 146 | return 0.5*(x - 1); 147 | } 148 | }; 149 | 150 | struct DoubleWell { 151 | template 152 | auto eval(const T x) const { 153 | return 9./16.*(x*x - 1.)*(x*x - 1); 154 | } 155 | 156 | template 157 | auto grad(const T x) const { 158 | return 9./4.*(x*x - 1)*x; 159 | } 160 | 161 | }; 162 | 163 | struct SmoothIndicatorFunction { 164 | template 165 | auto eval(const T x) const { 166 | return 0.25*(x + 1.)*(x + 1.); 167 | } 168 | 169 | template 170 | auto grad(const T x) const { 171 | return .5*(x + 1.); 172 | } 173 | }; 174 | 175 | struct WeightExitPenalty { 176 | 177 | const double eps = 1e-6; 178 | 179 | template 180 | T eval(const T x) const { 181 | return 1./(x + eps); 182 | } 183 | 184 | template 185 | T grad(const T x) const { 186 | return -1./Math::pow<2>(x+eps); 187 | } 188 | }; 189 | 190 | struct Quadratic { 191 | 192 | template 193 | constexpr T eval(const T x) const { 194 | return x*x; 195 | } 196 | 197 | template 198 | constexpr T grad(const T x) const { 199 | return 2*x; 200 | } 201 | }; 202 | 203 | struct Quartic { 204 | 205 | template 206 | constexpr T eval(const T x) const { 207 | if(x < -1) return 1; 208 | if(x < 1) return x*x*(2-x*x); 209 | return 1; 210 | } 211 | 212 | template 213 | constexpr T grad(const T x) const { 214 | if(x < -1) return 0; 215 | if(x < 1) return 2*x*(1-2*x*x); 216 | return 0; 217 | } 218 | }; 219 | 220 | -------------------------------------------------------------------------------- /Utilities/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(Utilities 2 | UniqueFunction.h 3 | SharedPointer.h 4 | FunctionRef.h 5 | SmartEnum.h 6 | StlAlgorithm.h 7 | YCombinator.h 8 | C1Functions.h 9 | Enums.h 10 | Heap.h 11 | CircularBuffer.h 12 | SparseMatrix.cpp 13 | SparseMatrix.h 14 | Utility.h 15 | Traits.h 16 | Algorithms.h 17 | ) 18 | 19 | target_include_directories(Utilities PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 20 | target_link_libraries(Utilities PUBLIC Corrade::Containers Magnum::Magnum) 21 | add_library(Phasefield::Utilities ALIAS Utilities) 22 | 23 | #add_executable(bench hash_map_bench.cpp) 24 | #target_link_libraries(bench PUBLIC utilities Magnum::Magnum Eigen3::Eigen) 25 | -------------------------------------------------------------------------------- /Utilities/CircularBuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 8/18/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace Phasefield { 10 | 11 | namespace Cr = Corrade; 12 | 13 | namespace { 14 | 15 | std::uint32_t nextPow2(std::uint32_t v) { 16 | v--; 17 | v |= v >> 1u; 18 | v |= v >> 2u; 19 | v |= v >> 4u; 20 | v |= v >> 8u; 21 | v |= v >> 16u; 22 | v++; 23 | return v; 24 | } 25 | 26 | } 27 | 28 | template 29 | class CircularBuffer { 30 | public: 31 | 32 | enum class RelocationOption : std::uint8_t { 33 | LeftBound = 1, 34 | RightBound = 2, 35 | Center = 3 36 | }; 37 | 38 | explicit CircularBuffer(std::size_t size) : m_data{Cr::Containers::DefaultInit, nextPow2(size)} { 39 | m_mask = m_data.size() - 1; 40 | } 41 | 42 | explicit CircularBuffer() = default; 43 | 44 | template 45 | void emplaceBack(Args&& ... args) { 46 | if(m_size + 1 > m_data.size()){ 47 | grow(RelocationOption::Center); 48 | } 49 | m_data[m_end] = T{static_cast(args)...}; 50 | m_end = m_mask & (m_end + 1); 51 | ++m_size; 52 | } 53 | 54 | template 55 | void emplaceFront(Args&& ... args) { 56 | if(m_size + 1 > m_data.size()){ 57 | grow(RelocationOption::Center); 58 | } 59 | m_begin = (m_begin - 1) & m_mask; 60 | m_data[m_begin] = T{static_cast(args)...}; 61 | ++m_size; 62 | } 63 | 64 | T popBack() { 65 | --m_size; 66 | m_end = m_mask & (m_end - 1); 67 | return std::move(m_data[m_end]); 68 | } 69 | 70 | T popFront() { 71 | --m_size; 72 | auto oldBegin = m_begin; 73 | m_begin = m_mask & (m_begin + 1); 74 | return std::move(m_data[oldBegin]); 75 | } 76 | 77 | void shrinkToFit() { 78 | auto size = nextPow2(m_size); 79 | if(size != m_data.size()) { 80 | Cr::Containers::Array data{Corrade::Containers::DefaultInit, size}; 81 | 82 | for(std::uint32_t i = 0; i < m_size; ++i) 83 | data[i] = std::move(m_data[(m_begin + i) & m_mask]); 84 | 85 | m_data = data; 86 | m_begin = 0; 87 | m_end = m_size; 88 | } 89 | } 90 | 91 | T& operator[](std::size_t idx) { 92 | return m_data[(m_begin + idx) & m_mask]; 93 | } 94 | 95 | const T& operator[](std::size_t idx) const { 96 | return m_data[(m_begin + idx) & m_mask]; 97 | } 98 | 99 | void grow(RelocationOption option = RelocationOption::Center){ 100 | Cr::Containers::Array data(Cr::Containers::DefaultInit, nextPow2(m_size + 1)); 101 | 102 | std::uint32_t offset; 103 | switch(option){ 104 | case RelocationOption::LeftBound : offset = 0; break; 105 | case RelocationOption::Center : offset = data.size()/2 - m_size/2; break; 106 | case RelocationOption::RightBound : offset = data.size() - m_size; break; 107 | } 108 | for(std::uint32_t i = 0; i < m_size; ++i) 109 | data[i + offset] = std::move(m_data[(m_begin + i) & m_mask]); 110 | 111 | m_data = std::move(data); 112 | m_begin = offset; 113 | m_end = m_size + offset; 114 | m_mask = m_data.size() - 1; 115 | } 116 | 117 | [[nodiscard]] std::uint32_t size() const { 118 | return m_size; 119 | } 120 | 121 | [[nodiscard]] bool empty() const { 122 | return m_size == 0; 123 | } 124 | 125 | void clear() { m_begin = 0; m_end = 0; m_size = 0; m_mask = 0; } 126 | 127 | private: 128 | std::uint32_t m_begin = 0, m_end = 0, m_size = 0; 129 | Cr::Containers::Array m_data; 130 | 131 | std::uint32_t m_mask = 0; 132 | }; 133 | 134 | 135 | } 136 | -------------------------------------------------------------------------------- /Utilities/Enums.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 02.05.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "SmartEnum.h" 8 | 9 | #include 10 | #include 11 | 12 | namespace Cr = Corrade; 13 | namespace Mg = Magnum; 14 | 15 | namespace Containers = Corrade::Containers; 16 | 17 | SMART_ENUM(ColorMapType, Mg::UnsignedInt, 18 | Turbo, 19 | Magma, 20 | Plasma, 21 | Inferno, 22 | Viridis) 23 | 24 | enum class ShaderType : Magnum::UnsignedInt { 25 | FlatTextured = 0, 26 | PhongDiffuseColored = 1, 27 | PhongDiffuse = 2, 28 | MeshVisualizer = 3, 29 | MeshVisualizerPrimitiveId = 4, 30 | VertexColor = 8, 31 | Phong 32 | }; 33 | 34 | SMART_ENUM(FunctionalType, Mg::UnsignedInt, 35 | DirichletEnergy, 36 | DoubleWellPotential, 37 | AreaRegularizer, 38 | ConnectednessConstraint, 39 | DiffuseYamabe, 40 | HierarchicalRegularization, 41 | Unknown) 42 | 43 | enum class DrawableType : Magnum::Int { 44 | MeshVisualizer = 0, 45 | PhongDiffuse = 1, 46 | FlatTextured = 2, 47 | FaceColored = 3, 48 | }; 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Utilities/FunctionRef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | template 6 | class FunctionRef; 7 | 8 | template 9 | class FunctionRef { 10 | Ret (* callback)(void* callable, Params ...params) = nullptr; 11 | void* callable; 12 | 13 | public: 14 | FunctionRef() = default; 15 | 16 | FunctionRef(std::nullptr_t) {} 17 | 18 | template 19 | FunctionRef(Callable&& callable) : callable(&callable) { 20 | callback = +[](void* c, Params... params) { return (*static_cast*>(c))(params...); }; 21 | } 22 | 23 | Ret operator()(Params ...params) const { 24 | return callback(callable, (Params&&) (params)...); 25 | } 26 | 27 | explicit operator bool() const { return callback; } 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /Utilities/Range.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/6/20. 3 | // 4 | 5 | #pragma once 6 | 7 | namespace Phasefield { 8 | 9 | template 10 | struct Range { 11 | It b, e; 12 | It begin() { return b; } 13 | It end() { return e; } 14 | }; 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Utilities/Serialize.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/9/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Types.h" 8 | #include 9 | 10 | template 11 | void serializeTrivial(Array& data, T const& x) { 12 | arrayAppend(data, {&x, sizeof(std::remove_reference_t)}); 13 | } 14 | 15 | 16 | template 17 | T deserializeTrivial(const char*& pc) { 18 | T t; 19 | memcpy(&t, pc, sizeof(T)); 20 | pc += sizeof(T); 21 | return t; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Utilities/SharedPointer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 29.04.20. 3 | // 4 | // barebones (e.g. not thread safe) shared_ptr for trivial types 5 | // 6 | 7 | #pragma once 8 | 9 | #include //for is_trivial 10 | #include //for move 11 | #include //for memcpy 12 | #include //for malloc 13 | #include 14 | 15 | template 16 | struct SharedPointer { 17 | 18 | static_assert(std::is_trivial_v, "Shared Ressource : T needs to be trivial"); 19 | struct Block { 20 | T x; 21 | size_t refCount; 22 | }; 23 | Block* data = nullptr; 24 | 25 | SharedPointer(std::nullptr_t) : data(nullptr) {} 26 | 27 | explicit SharedPointer(T const& x) { 28 | data = (Block*) std::malloc(sizeof(Block)); 29 | std::memcpy(&data->x, &x, sizeof(T)); 30 | data->refCount = 1; 31 | } 32 | 33 | SharedPointer& operator=(SharedPointer other) noexcept { 34 | other.swap(*this); 35 | return *this; 36 | } 37 | 38 | SharedPointer(SharedPointer const& other) noexcept: data(other.data) { 39 | ++(data->refCount); 40 | } 41 | 42 | SharedPointer(SharedPointer&& other) noexcept: data(other.data) { 43 | other.data = nullptr; 44 | } 45 | 46 | void swap(SharedPointer& other) { 47 | auto temp = other.data; 48 | other.data = data; 49 | data = temp; 50 | } 51 | 52 | T& operator*() { return data->x; } 53 | 54 | T const& operator*() const { return data->x; } 55 | 56 | explicit operator bool() const { return data != nullptr; } 57 | 58 | T* get() { return &data->x; } 59 | 60 | T const* get() const { return &data->x; } 61 | 62 | int refCount() { return data->refCount; } 63 | 64 | ~SharedPointer() { 65 | if(!data) return; 66 | assert(data->refCount); 67 | if(!(--(data->refCount))) 68 | delete data; 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /Utilities/SmallArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 12.04.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace Cr = Corrade; 10 | 11 | template 12 | class SmallArray { 13 | public: 14 | using value_type = T; 15 | using reference = T&; 16 | using const_reference = T const&; 17 | using iterator = T*; 18 | using const_iterator = T const*; 19 | using difference_type = std::ptrdiff_t; 20 | using size_type = int; 21 | 22 | 23 | explicit SmallArray(Cr::Containers::DefaultInitT) : m_data{Cr::Containers::DefaultInit} {} 24 | 25 | explicit SmallArray(Cr::Containers::ValueInitT) : m_data{Cr::Containers::ValueInit} {} 26 | 27 | explicit SmallArray(Cr::Containers::NoInitT) : m_data(Cr::Containers::NoInit) {} 28 | 29 | iterator begin() { return m_data.data(); } 30 | 31 | const_iterator begin() const { return m_data.data(); } 32 | 33 | iterator end() { return m_data.data() + m_size; } 34 | 35 | const_iterator end() const { return m_data.data() + m_size; } 36 | 37 | reference operator[](int i) { 38 | return m_data[i]; 39 | } 40 | 41 | const_reference operator[](int i) const { 42 | return m_data[i]; 43 | } 44 | 45 | template 46 | void emplaceBack(Args&& ... args) { 47 | m_data[m_size++] = T((Args&&) args...); 48 | } 49 | 50 | void pushBack(T&& x) { 51 | m_data[m_size++] = (T&&) x; 52 | } 53 | 54 | [[nodiscard]] size_type size() const { return m_size; } 55 | 56 | private: 57 | 58 | Corrade::Containers::StaticArray m_data; 59 | std::size_t m_size = 0; 60 | }; -------------------------------------------------------------------------------- /Utilities/SmartEnum.h: -------------------------------------------------------------------------------- 1 | #define STRINGIZE(arg) STRINGIZE1(arg) 2 | #define STRINGIZE1(arg) STRINGIZE2(arg) 3 | #define STRINGIZE2(arg) #arg 4 | 5 | #define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) 6 | #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) 7 | #define CONCATENATE2(arg1, arg2) arg1##arg2 8 | 9 | #define FOR_EACH_1(what, x) what(x) 10 | #define FOR_EACH_2(what, x, ...)\ 11 | what(x);\ 12 | FOR_EACH_1(what, __VA_ARGS__); 13 | #define FOR_EACH_3(what, x, ...)\ 14 | what(x);\ 15 | FOR_EACH_2(what, __VA_ARGS__); 16 | #define FOR_EACH_4(what, x, ...)\ 17 | what(x);\ 18 | FOR_EACH_3(what, __VA_ARGS__); 19 | #define FOR_EACH_5(what, x, ...)\ 20 | what(x);\ 21 | FOR_EACH_4(what, __VA_ARGS__); 22 | #define FOR_EACH_6(what, x, ...)\ 23 | what(x);\ 24 | FOR_EACH_5(what, __VA_ARGS__); 25 | #define FOR_EACH_7(what, x, ...)\ 26 | what(x);\ 27 | FOR_EACH_6(what, __VA_ARGS__); 28 | #define FOR_EACH_8(what, x, ...)\ 29 | what(x);\ 30 | FOR_EACH_7(what, __VA_ARGS__); 31 | #define FOR_EACH_9(what, x, ...)\ 32 | what(x);\ 33 | FOR_EACH_8(what, __VA_ARGS__); 34 | #define FOR_EACH_10(what, x, ...)\ 35 | what(x);\ 36 | FOR_EACH_9(what, __VA_ARGS__); 37 | #define FOR_EACH_11(what, x, ...)\ 38 | what(x);\ 39 | FOR_EACH_10(what, __VA_ARGS__); 40 | #define FOR_EACH_12(what, x, ...)\ 41 | what(x);\ 42 | FOR_EACH_11(what, __VA_ARGS__); 43 | #define FOR_EACH_13(what, x, ...)\ 44 | what(x);\ 45 | FOR_EACH_12(what, __VA_ARGS__); 46 | #define FOR_EACH_14(what, x, ...)\ 47 | what(x);\ 48 | FOR_EACH_13(what, __VA_ARGS__); 49 | #define FOR_EACH_15(what, x, ...)\ 50 | what(x);\ 51 | FOR_EACH_14(what, __VA_ARGS__); 52 | #define FOR_EACH_16(what, x, ...)\ 53 | what(x);\ 54 | FOR_EACH_15(what, __VA_ARGS__); 55 | 56 | #define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) 57 | #define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) 58 | #define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N 59 | #define FOR_EACH_RSEQ_N() 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 60 | 61 | #define FOR_EACH_(N, what, x, ...) CONCATENATE(FOR_EACH_, N)(what, x, __VA_ARGS__) 62 | #define FOR_EACH(what, x, ...) FOR_EACH_(FOR_EACH_NARG(x, __VA_ARGS__), what, x, __VA_ARGS__) 63 | 64 | #define SMART_ENUM_SWITCH_STATEMENT(e) case e : return #e; 65 | 66 | #define SMART_ENUM(name, underlying_type, ...) \ 67 | namespace name { \ 68 | enum Value : underlying_type { \ 69 | __VA_ARGS__ \ 70 | }; \ 71 | constexpr char const* to_string(Value x){ \ 72 | switch(x){ \ 73 | FOR_EACH(SMART_ENUM_SWITCH_STATEMENT, __VA_ARGS__) \ 74 | default : return "Unknow Enum Type";\ 75 | } \ 76 | } \ 77 | constexpr Value range[] = {__VA_ARGS__}; \ 78 | } 79 | -------------------------------------------------------------------------------- /Utilities/SparseMatrix.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 7/12/20. 3 | // 4 | 5 | #include "SparseMatrix.h" 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace Phasefield { 12 | 13 | Containers::ArrayView SparseMatrix::row(std::size_t r) { 14 | auto b = std::lower_bound(rows.begin(), rows.end(), r); 15 | auto e = std::upper_bound(b, rows.end(), r); 16 | return {values.begin() + (b - rows.begin()), std::size_t(e - b)}; 17 | } 18 | 19 | 20 | Containers::Array SparseMatrix::reduceRowwise() { 21 | Containers::Array rowsum(Containers::ValueInit, numCols); 22 | for(int i = 0; i < nnz; ++i) { 23 | rowsum[cols[i]] += values[i]; 24 | } 25 | return rowsum; 26 | } 27 | 28 | void SparseMatrix::clear() { 29 | nnz = 0; 30 | Containers::arrayResize(rows, Containers::NoInit, 0); 31 | Containers::arrayResize(cols, Containers::NoInit, 0); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /Utilities/SparseMatrix.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 6/25/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Enums.h" 8 | #include "Types.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace Phasefield { 15 | 16 | namespace Mg = Magnum; 17 | 18 | struct Triplet { 19 | size_t row, column; 20 | double value; 21 | }; 22 | 23 | struct SparseMatrix { 24 | 25 | SparseMatrix() = default; 26 | 27 | explicit SparseMatrix(Array triplets); 28 | 29 | size_t numRows, numCols, nnz; 30 | 31 | Array values; 32 | Array rows; 33 | Array cols; 34 | 35 | struct RowRange { 36 | int current, rowEnd; 37 | }; 38 | 39 | ArrayView row(std::size_t r); 40 | 41 | void clear(); 42 | 43 | Array reduceRowwise(); 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Utilities/StlAlgorithm.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 06.06.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | /* In c++20 the stl header transitivly includes the header which is huge */ 10 | #ifdef CORRADE_TARGET_LIBSTDCXX 11 | #include 12 | #include 13 | #else 14 | #include 15 | #endif 16 | -------------------------------------------------------------------------------- /Utilities/Traits.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 11/2/20. 3 | // 4 | 5 | #pragma once 6 | 7 | namespace Phasefield { 8 | 9 | template struct RemoveReference {typedef T type;}; 10 | template struct RemoveReference {typedef T type;}; 11 | template struct RemoveReference {typedef T type;}; 12 | template using RemoveReferenceT = typename RemoveReference::type; 13 | 14 | #define MOVE(x) static_cast&&>(x) 15 | #define FWD(x) static_cast(x) 16 | 17 | } -------------------------------------------------------------------------------- /Utilities/Types.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 8/26/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace Corrade::Containers; 12 | using namespace Magnum::Shaders; 13 | 14 | using Magnum::UnsignedInt; 15 | using Magnum::Float; 16 | using Magnum::Double; 17 | using Magnum::Int; 18 | using Magnum::UnsignedByte; 19 | using Magnum::UnsignedLong; 20 | 21 | using Magnum::Vector3; 22 | using Magnum::Vector2; 23 | using Magnum::Vector2d; 24 | using Magnum::Vector2i; 25 | using Magnum::Vector2ui; 26 | using Magnum::Vector3ub; 27 | using Magnum::Vector3ui; 28 | using Magnum::Vector3d; 29 | using Magnum::Vector4; 30 | using Magnum::Matrix3; 31 | using Magnum::Matrix3d; 32 | using Magnum::Matrix4; 33 | using Magnum::Color3; 34 | using Magnum::Color3ub; 35 | using Magnum::Color4; 36 | using Magnum::Color4ub; 37 | using Magnum::Range2D; 38 | using Magnum::Range2Di; 39 | using Magnum::Range3D; 40 | 41 | using Magnum::Rad; 42 | using Magnum::Radd; 43 | using Magnum::Deg; 44 | 45 | using Magnum::Debug; 46 | 47 | namespace Math = Magnum::Math; 48 | namespace GL = Magnum::GL; 49 | 50 | namespace Phasefield { 51 | constexpr size_t Invalid = ~size_t{0}; 52 | } 53 | -------------------------------------------------------------------------------- /Utilities/UniqueFunction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Allocate.h" 4 | #include "Utility.h" 5 | 6 | namespace Phasefield { 7 | 8 | template 9 | class UniqueFunction; 10 | 11 | template 12 | class UniqueFunction { 13 | public: 14 | 15 | UniqueFunction() = default; 16 | 17 | UniqueFunction(std::nullptr_t) {} 18 | 19 | template 20 | UniqueFunction(Callable&& f) { 21 | 22 | erased = allocate_buffer(sizeof(Callable), alignof(Callable)); 23 | ::new(erased) Callable((Callable&&) f); 24 | 25 | destroy = +[](void* e) { 26 | static_cast(e)->~Callable(); 27 | deallocate_buffer(e, sizeof(Callable), alignof(Callable)); 28 | }; 29 | 30 | call = +[](void* e, Params... params) -> Ret { 31 | return (*static_cast(e))(params...); 32 | }; 33 | } 34 | 35 | UniqueFunction& operator=(UniqueFunction&& other) noexcept { 36 | other.swap(*this); 37 | return *this; 38 | } 39 | 40 | UniqueFunction(UniqueFunction&& other) noexcept { 41 | other.swap(*this); 42 | } 43 | 44 | void swap(UniqueFunction& other) { 45 | pf_swap(erased, other.erased); 46 | pf_swap(destroy, other.destroy); 47 | pf_swap(call, other.call); 48 | } 49 | 50 | Ret operator()(Params ...params) const { 51 | return call(erased, (Params&&) (params)...); 52 | } 53 | 54 | explicit operator bool() const { return erased; } 55 | 56 | ~UniqueFunction() { 57 | if(destroy) destroy(erased); 58 | } 59 | 60 | private: 61 | 62 | void* erased = nullptr; 63 | void (* destroy)(void*) = nullptr; 64 | Ret (* call)(void*, Params...) = nullptr; 65 | }; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /Utilities/Utility.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 11/2/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Traits.h" 8 | 9 | namespace Phasefield { 10 | 11 | template 12 | struct Pair { 13 | T1 first; 14 | T2 second; 15 | }; 16 | 17 | template 18 | void pf_swap(T& a, T& b) { 19 | T tmp = MOVE(a); 20 | a = MOVE(b); 21 | b = MOVE(tmp); 22 | } 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Utilities/YCombinator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 08.06.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | template 10 | struct YCombinator { 11 | F&& f; 12 | 13 | template 14 | decltype(auto) operator()(Args&& ... args) const { 15 | return f(*this, static_cast(args)...); 16 | } 17 | }; 18 | 19 | /* deduction guide */ 20 | template 21 | YCombinator(F&& f) -> YCombinator>; -------------------------------------------------------------------------------- /Utilities/test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 22.05.20. 3 | // 4 | 5 | #include "HashMap.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace Magnum; 16 | 17 | struct Edge { 18 | UnsignedInt a, b; 19 | 20 | bool operator==(const Edge& other) const { 21 | return a == other.a && b == other.b; 22 | } 23 | 24 | std::size_t hash() const { 25 | const UnsignedLong ab = UnsignedLong(a) + (UnsignedLong(b) << 32ul); 26 | return *reinterpret_cast(Utility::MurmurHash2{}(reinterpret_cast(&ab), sizeof(UnsignedLong)).byteArray()); 27 | } 28 | }; 29 | 30 | struct HalfEdge { 31 | 32 | 33 | UnsignedInt next; 34 | UnsignedInt face; 35 | UnsignedInt opposite; 36 | UnsignedInt vertex; 37 | }; 38 | 39 | struct Face { 40 | UnsignedInt halfEdge; 41 | UnsignedInt degree; 42 | }; 43 | 44 | struct Mesh { 45 | 46 | enum class Attribute : UnsignedShort { 47 | Position = 1, 48 | Tangent, 49 | Bitangent, 50 | Normal, 51 | TextureCoordinates, 52 | Color 53 | } 54 | 55 | using halfedgedescriptor = unsignedint; 56 | using vertexdescriptor = unsignedint; 57 | using facedescriptor = unsignedint; 58 | 59 | Containers::Array vertexData; 60 | Containers::Array indexData; 61 | 62 | Containers::Array attributes; 63 | 64 | Containers::Array faces; 65 | Containers::Array halfedges; 66 | 67 | Mesh(Trade::MeshData meshData){ 68 | 69 | vertexData = meshData.releaseVertexData(); 70 | attributes = meshData.releaseAttributeData(); 71 | 72 | auto hash = [](Edge const& e) noexcept { return e.hash(); }; 73 | Containers::HashMap edges(meshData.indexCount()); 74 | 75 | //HashMap edges(meshData.indexCount(), hash); 76 | auto triangles = Containers::arrayCast(indexData); 77 | 78 | for(int i = 0; i < triangles.size(); ++i){ 79 | for(int j = 0; j < 3; ++j){ 80 | auto& he = halfedges[3*i + j]; 81 | he.face = i; 82 | auto k = (j + 1)%3; 83 | he.next = 3*i + k; 84 | he.vertex = triangles[i][k]; 85 | 86 | auto [it, inserted] = edges.tryEmplace(Edge{triangles[i][j], triangles[i][k]}, i); 87 | if(!inserted){ 88 | auto at = it->; /* adjacent triangle */ 89 | for(int l = 0; l < 3; ++l){ 90 | if(halfedges[3*at + l].next == triangles[i][j]){ 91 | he.opposite = 3*at + l; 92 | halfedges[3*at + l].opposite = 3*i + j; 93 | break; 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | 101 | VertexDescriptor addVertex(){ 102 | 103 | } 104 | 105 | HalfEdgeDescriptor addEdge(VertexDescriptor a, VertexDescriptor b){ 106 | 107 | } 108 | 109 | 110 | void requireEdgeLengths(){ 111 | 112 | } 113 | 114 | void requireCornerAngles(){ 115 | 116 | } 117 | 118 | }; 119 | 120 | int main() { 121 | 122 | 123 | } -------------------------------------------------------------------------------- /VideoSaver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(VideoSaver VideoSaver.cpp VideoSaver.h) 3 | 4 | target_include_directories(VideoSaver PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 5 | add_library(Phasefield::VideoSaver ALIAS VideoSaver) 6 | 7 | target_link_libraries(VideoSaver 8 | PUBLIC 9 | PkgConfig::libavcodec 10 | PkgConfig::libavutil 11 | PkgConfig::libswscale 12 | Corrade::Containers 13 | Phasefield::Utilities 14 | ) 15 | 16 | -------------------------------------------------------------------------------- /VideoSaver/VideoSaver.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/14/20. 3 | // 4 | 5 | #include "VideoSaver.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace Phasefield { 13 | 14 | namespace { 15 | 16 | void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt, FILE *outfile) { 17 | int ret; 18 | 19 | /* send the frame to the encoder */ 20 | 21 | ret = avcodec_send_frame(enc_ctx, frame); 22 | if (ret < 0) { 23 | fprintf(stderr, "Error sending a frame for encoding\n"); 24 | exit(1); 25 | } 26 | 27 | while (ret >= 0) { 28 | ret = avcodec_receive_packet(enc_ctx, pkt); 29 | if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) 30 | return; 31 | else if (ret < 0) { 32 | fprintf(stderr, "Error during encoding\n"); 33 | exit(1); 34 | } 35 | 36 | fwrite(pkt->data, 1, pkt->size, outfile); 37 | av_packet_unref(pkt); 38 | } 39 | } 40 | 41 | } 42 | 43 | void VideoSaver::endRecording() { 44 | { 45 | std::unique_lock lock(m_mutex); 46 | m_finished = true; 47 | m_cv.notify_all(); 48 | } 49 | 50 | m_thread.join(); 51 | 52 | /* flush the encoder */ 53 | encode(c, nullptr, pkt, outfile); 54 | /* add sequence end code to have a real MPEG file */ 55 | if (codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO) 56 | fwrite(endcode, 1, sizeof(endcode), outfile); 57 | fclose(outfile); 58 | 59 | avcodec_free_context(&c); 60 | av_frame_free(&frame); 61 | av_packet_free(&pkt); 62 | 63 | 64 | sws_freeContext(sws_ctx); 65 | } 66 | 67 | void VideoSaver::work() { 68 | while(true) { 69 | Optional job; 70 | { 71 | std::unique_lock lock(m_mutex); 72 | while(m_buffer.empty()){ 73 | if(m_finished) return; 74 | m_cv.wait(lock); 75 | } 76 | job = std::move(m_buffer.front()); 77 | m_buffer.pop(); 78 | } 79 | encodeSingleFrame(*job); 80 | } 81 | } 82 | 83 | void VideoSaver::appendFrame(Mg::Image2D&& currentFrame) { 84 | std::lock_guard lock(m_mutex); 85 | bool wake = m_buffer.empty(); 86 | m_buffer.emplace(std::move(currentFrame)); 87 | if (wake) m_cv.notify_one(); 88 | } 89 | 90 | void VideoSaver::startRecording(const char* path, const Vector2i& size) { 91 | 92 | const char *codec_name = "libx265"; 93 | int ret; 94 | 95 | /* find the mpeg1video encoder */ 96 | codec = avcodec_find_encoder_by_name(codec_name); 97 | if (!codec) { 98 | fprintf(stderr, "Codec '%s' not found\n", codec_name); 99 | exit(1); 100 | } 101 | 102 | c = avcodec_alloc_context3(codec); 103 | if (!c) { 104 | fprintf(stderr, "Could not allocate video codec context\n"); 105 | exit(1); 106 | } 107 | 108 | pkt = av_packet_alloc(); 109 | if (!pkt) 110 | exit(1); 111 | 112 | /* put sample parameters */ 113 | c->bit_rate = 400000; 114 | /* resolution must be a multiple of two */ 115 | CORRADE_INTERNAL_ASSERT(size.x()%2 == 0 && size.y()%2 == 0); 116 | c->width = size.x(); 117 | c->height = size.y(); 118 | c->thread_count = 8; 119 | 120 | /* frames per second */ 121 | c->time_base = (AVRational){1, 25}; 122 | c->framerate = (AVRational){25, 1}; 123 | 124 | /* emit one intra frame every ten frames 125 | * check frame pict_type before passing frame 126 | * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I 127 | * then gop_size is ignored and the output of encoder 128 | * will always be I frame irrespective to gop_size 129 | */ 130 | c->gop_size = 10; 131 | c->max_b_frames = 1; 132 | c->pix_fmt = AV_PIX_FMT_YUV420P; 133 | 134 | if (codec->id == AV_CODEC_ID_H264) 135 | av_opt_set(c->priv_data, "preset", "slow", 0); 136 | 137 | /* open it */ 138 | ret = avcodec_open2(c, codec, nullptr); 139 | if (ret < 0) { 140 | Debug{} << "Could not open codec"; 141 | exit(1); 142 | } 143 | 144 | outfile = fopen(path, "wb"); 145 | if (!outfile) { 146 | Debug{} << "Could not open" << path; 147 | exit(1); 148 | } 149 | 150 | frame = av_frame_alloc(); 151 | if (!frame) { 152 | Debug{} << "Could not allocate video frame"; 153 | exit(1); 154 | } 155 | frame->format = c->pix_fmt; 156 | frame->width = c->width; 157 | frame->height = c->height; 158 | 159 | ret = av_frame_get_buffer(frame, 32); 160 | if (ret < 0) { 161 | Debug{} << "Could not allocate the video frame data"; 162 | exit(1); 163 | } 164 | 165 | /* create scaling context */ 166 | sws_ctx = sws_getContext(size.x(), size.y(), AV_PIX_FMT_RGB24, 167 | size.x(), size.y(), AV_PIX_FMT_YUV420P, 168 | SWS_BILINEAR, nullptr, nullptr, nullptr); 169 | if(!sws_ctx) { 170 | Debug{} << "Could not create scaling context"; 171 | } 172 | 173 | /* allocate source and destination image buffers */ 174 | ret = av_image_alloc(m_data, m_line_size, size.x(), size.y(), AV_PIX_FMT_RGB24, 32); 175 | if (ret < 0) { 176 | Debug{} << "Could not allocate source image buffer"; 177 | } 178 | 179 | /* spin up worker thread */ 180 | m_thread = std::thread([this]{ work(); }); 181 | } 182 | 183 | void VideoSaver::encodeSingleFrame(Mg::Image2D const& image) { 184 | /* make sure the frame data is writable */ 185 | int ret = av_frame_make_writable(frame); 186 | if (ret < 0) 187 | exit(1); 188 | 189 | auto pixel = image.pixels().transposed<0, 1>().flipped<1>(); 190 | 191 | for (size_t y = 0; y < c->height; y++) { 192 | for (size_t x = 0; x < c->width; x++) { 193 | m_data[0][y*m_line_size[0] + 3*x] = pixel[x][y].r(); 194 | m_data[0][y*m_line_size[0] + 3*x + 1] = pixel[x][y].g(); 195 | m_data[0][y*m_line_size[0] + 3*x + 2] = pixel[x][y].b(); 196 | } 197 | } 198 | 199 | /* convert to destination format */ 200 | sws_scale(sws_ctx, (const uint8_t * const*)m_data, 201 | m_line_size, 0, frame->height, frame->data, frame->linesize); 202 | 203 | frame->pts = encodedFrames++; 204 | 205 | /* encode the image */ 206 | encode(c, frame, pkt, outfile); 207 | } 208 | 209 | } -------------------------------------------------------------------------------- /VideoSaver/VideoSaver.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/14/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Types.h" 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | extern "C" { 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | } 24 | 25 | namespace Phasefield { 26 | 27 | namespace Mg = Magnum; 28 | 29 | /** 30 | * aynchronous video saver 31 | */ 32 | 33 | class VideoSaver { 34 | public: 35 | 36 | void startRecording(const char* path, Vector2i const& size); 37 | 38 | void endRecording(); 39 | 40 | void appendFrame(Mg::Image2D&& frame); 41 | 42 | void encodeSingleFrame(Mg::Image2D const& frame); 43 | 44 | void work(); 45 | 46 | private: 47 | 48 | std::thread m_thread; 49 | std::mutex m_mutex; 50 | std::condition_variable m_cv; 51 | bool m_finished = false; 52 | std::queue m_buffer; 53 | 54 | AVCodecContext* enc_ctx = nullptr; 55 | AVFrame* frame = nullptr; 56 | AVPacket* pkt = nullptr; 57 | AVCodecContext *c = nullptr; 58 | const AVCodec *codec; 59 | SwsContext *sws_ctx; 60 | FILE* outfile = nullptr; 61 | 62 | static constexpr uint8_t endcode[] = { 0, 0, 1, 0xb7 }; 63 | 64 | size_t encodedFrames = 0; 65 | 66 | uint8_t *m_data[4]; 67 | int m_line_size[4]; 68 | }; 69 | 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /Viewer/ArcBall.cpp: -------------------------------------------------------------------------------- 1 | #include "ArcBall.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace Magnum; 7 | 8 | namespace { 9 | 10 | /* Project a point in NDC onto the arcball sphere */ 11 | Quaternion ndcToArcBall(const Vector2& p) { 12 | const Float dist = Math::dot(p, p); 13 | 14 | /* Point is on sphere */ 15 | if(dist <= 1.0f) 16 | return {{p.x(), p.y(), Math::sqrt(1.0f - dist)}, 0.0f}; 17 | 18 | /* Point is outside sphere */ 19 | else{ 20 | const Vector2 proj = p.normalized(); 21 | return {{proj.x(), proj.y(), 0.0f}, 0.0f}; 22 | } 23 | } 24 | 25 | } 26 | 27 | ArcBall::ArcBall(const Vector3& eye, const Vector3& viewCenter, 28 | const Vector3& upDir, Deg fov, const Vector2i& windowSize) : 29 | _fov{fov}, _windowSize{windowSize} { 30 | setViewParameters(eye, viewCenter, upDir); 31 | } 32 | 33 | void ArcBall::setViewParameters(const Vector3& eye, const Vector3& viewCenter, 34 | const Vector3& upDir) { 35 | const Vector3 dir = viewCenter - eye; 36 | Vector3 zAxis = dir.normalized(); 37 | Vector3 xAxis = (Math::cross(zAxis, upDir.normalized())).normalized(); 38 | Vector3 yAxis = (Math::cross(xAxis, zAxis)).normalized(); 39 | xAxis = (Math::cross(zAxis, yAxis)).normalized(); 40 | 41 | _targetPosition = -viewCenter; 42 | _targetZooming = -dir.length(); 43 | _targetQRotation = Quaternion::fromMatrix( 44 | Matrix3x3{xAxis, yAxis, -zAxis}.transposed()).normalized(); 45 | 46 | _positionT0 = _currentPosition = _targetPosition; 47 | _zoomingT0 = _currentZooming = _targetZooming; 48 | _qRotationT0 = _currentQRotation = _targetQRotation; 49 | 50 | updateInternalTransformations(); 51 | } 52 | 53 | void ArcBall::reset() { 54 | _targetPosition = _positionT0; 55 | _targetZooming = _zoomingT0; 56 | _targetQRotation = _qRotationT0; 57 | } 58 | 59 | void ArcBall::setLagging(const Float lagging) { 60 | CORRADE_INTERNAL_ASSERT(lagging >= 0.0f && lagging < 1.0f); 61 | _lagging = lagging; 62 | } 63 | 64 | void ArcBall::initTransformation(const Vector2i& mousePos) { 65 | _prevMousePosNDC = screenCoordToNDC(mousePos); 66 | } 67 | 68 | void ArcBall::rotate(const Vector2i& mousePos) { 69 | const Vector2 mousePosNDC = screenCoordToNDC(mousePos); 70 | const Quaternion currentQRotation = ndcToArcBall(mousePosNDC); 71 | const Quaternion prevQRotation = ndcToArcBall(_prevMousePosNDC); 72 | _prevMousePosNDC = mousePosNDC; 73 | _targetQRotation = 74 | (currentQRotation*prevQRotation*_targetQRotation).normalized(); 75 | } 76 | 77 | void ArcBall::translate(const Vector2i& mousePos) { 78 | const Vector2 mousePosNDC = screenCoordToNDC(mousePos); 79 | const Vector2 translationNDC = mousePosNDC - _prevMousePosNDC; 80 | _prevMousePosNDC = mousePosNDC; 81 | translateDelta(translationNDC); 82 | } 83 | 84 | void ArcBall::translateDelta(const Vector2& translationNDC) { 85 | /* Half size of the screen viewport at the view center and perpendicular 86 | with the viewDir */ 87 | const Float hh = Math::abs(_targetZooming)*Math::tan(_fov*0.5f); 88 | const Float hw = hh*Vector2{_windowSize}.aspectRatio(); 89 | 90 | _targetPosition += _inverseView.transformVector( 91 | {translationNDC.x()*hw, translationNDC.y()*hh, 0.0f}); 92 | } 93 | 94 | void ArcBall::zoom(const Float delta) { 95 | _targetZooming += delta; 96 | } 97 | 98 | bool ArcBall::updateTransformation() { 99 | const Vector3 diffViewCenter = _targetPosition - _currentPosition; 100 | const Quaternion diffRotation = _targetQRotation - _currentQRotation; 101 | const Float diffZooming = _targetZooming - _currentZooming; 102 | 103 | const Float dViewCenter = Math::dot(diffViewCenter, diffViewCenter); 104 | const Float dRotation = Math::dot(diffRotation, diffRotation); 105 | const Float dZooming = diffZooming*diffZooming; 106 | 107 | /* Nothing change */ 108 | if(dViewCenter < 1.0e-10f && 109 | dRotation < 1.0e-10f && 110 | dZooming < 1.0e-10f){ 111 | return false; 112 | } 113 | 114 | /* Nearly done: just jump directly to the target */ 115 | if(dViewCenter < 1.0e-6f && 116 | dRotation < 1.0e-6f && 117 | dZooming < 1.0e-6f){ 118 | _currentPosition = _targetPosition; 119 | _currentQRotation = _targetQRotation; 120 | _currentZooming = _targetZooming; 121 | 122 | /* Interpolate between the current transformation and the target 123 | transformation */ 124 | } else{ 125 | const Float t = 1 - _lagging; 126 | _currentPosition = Math::lerp(_currentPosition, _targetPosition, t); 127 | _currentZooming = Math::lerp(_currentZooming, _targetZooming, t); 128 | _currentQRotation = Math::slerpShortestPath( 129 | _currentQRotation, _targetQRotation, t); 130 | } 131 | 132 | updateInternalTransformations(); 133 | return true; 134 | } 135 | 136 | void ArcBall::updateInternalTransformations() { 137 | _view = DualQuaternion::translation(Vector3::zAxis(_currentZooming))* 138 | DualQuaternion{_currentQRotation}* 139 | DualQuaternion::translation(_currentPosition); 140 | _inverseView = _view.inverted(); 141 | } 142 | 143 | Vector2 ArcBall::screenCoordToNDC(const Vector2i& mousePos) const { 144 | return {mousePos.x()*2.0f/_windowSize.x() - 1.0f, 145 | 1.0f - 2.0f*mousePos.y()/_windowSize.y()}; 146 | } 147 | 148 | 149 | -------------------------------------------------------------------------------- /Viewer/ArcBall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Mg = Magnum; 8 | 9 | /* Implementation of Ken Shoemake's arcball camera with smooth navigation 10 | feature: https://www.talisman.org/~erlkonig/misc/shoemake92-arcball.pdf */ 11 | class ArcBall { 12 | public: 13 | ArcBall(const Mg::Vector3& cameraPosition, const Mg::Vector3& viewCenter, 14 | const Mg::Vector3& upDir, Mg::Deg fov, const Mg::Vector2i& windowSize); 15 | 16 | /* Set the camera view parameters: eye position, view center, up 17 | direction */ 18 | void setViewParameters(const Mg::Vector3& eye, const Mg::Vector3& viewCenter, 19 | const Mg::Vector3& upDir); 20 | 21 | /* Reset the camera to its initial position, view center, and up dir */ 22 | void reset(); 23 | 24 | /* Update screen size after the window has been resized */ 25 | void reshape(const Mg::Vector2i& windowSize) { _windowSize = windowSize; } 26 | 27 | /* Update any unfinished transformation due to lagging, return true if 28 | the camera matrices have changed */ 29 | bool updateTransformation(); 30 | 31 | /* Get/set the amount of lagging such that the camera will (slowly) 32 | smoothly navigate. Lagging must be in [0, 1) */ 33 | Mg::Float lagging() const { return _lagging; } 34 | 35 | void setLagging(Mg::Float lagging); 36 | 37 | /* Initialize the first (screen) mouse position for camera 38 | transformation. This should be called in mouse pressed event. */ 39 | void initTransformation(const Mg::Vector2i& mousePos); 40 | 41 | /* Rotate the camera from the previous (screen) mouse position to the 42 | current (screen) position */ 43 | void rotate(const Mg::Vector2i& mousePos); 44 | 45 | /* Translate the camera from the previous (screen) mouse position to 46 | the current (screen) mouse position */ 47 | void translate(const Mg::Vector2i& mousePos); 48 | 49 | /* Translate the camera by the delta amount of (NDC) mouse position. 50 | Note that NDC position must be in [-1, -1] to [1, 1]. */ 51 | void translateDelta(const Mg::Vector2& translationNDC); 52 | 53 | /* Zoom the camera (positive delta = zoom in, negative = zoom out) */ 54 | void zoom(Mg::Float delta); 55 | 56 | /* Get the camera's view transformation as a qual quaternion */ 57 | const Mg::DualQuaternion& view() const { return _view; } 58 | 59 | /* Get the camera's view transformation as a matrix */ 60 | Mg::Matrix4 viewMatrix() const { return _view.toMatrix(); } 61 | 62 | /* Get the camera's inverse view matrix (which also produces 63 | transformation of the camera) */ 64 | Mg::Matrix4 inverseViewMatrix() const { return _inverseView.toMatrix(); } 65 | 66 | /* Get the camera's transformation as a dual quaternion */ 67 | const Mg::DualQuaternion& transformation() const { return _inverseView; } 68 | 69 | /* Get the camera's transformation matrix */ 70 | Mg::Matrix4 transformationMatrix() const { return _inverseView.toMatrix(); } 71 | 72 | /* Return the distance from the camera position to the center view */ 73 | Mg::Float viewDistance() const { return Mg::Math::abs(_targetZooming); } 74 | 75 | protected: 76 | /* Update the camera transformations */ 77 | void updateInternalTransformations(); 78 | 79 | /* Transform from screen coordinate to NDC - normalized device 80 | coordinate. The top-left of the screen corresponds to [-1, 1] NDC, 81 | and the bottom right is [1, -1] NDC. */ 82 | Mg::Vector2 screenCoordToNDC(const Mg::Vector2i& mousePos) const; 83 | 84 | Mg::Deg _fov; 85 | Mg::Vector2i _windowSize; 86 | 87 | Mg::Vector2 _prevMousePosNDC; 88 | Mg::Float _lagging{}; 89 | 90 | Mg::Vector3 _targetPosition, _currentPosition, _positionT0; 91 | Mg::Quaternion _targetQRotation, _currentQRotation, _qRotationT0; 92 | Mg::Float _targetZooming, _currentZooming, _zoomingT0; 93 | Mg::DualQuaternion _view, _inverseView; 94 | }; 95 | 96 | -------------------------------------------------------------------------------- /Viewer/Bvh.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 10/11/20. 3 | // 4 | 5 | #include "Bvh.h" 6 | #include "Mesh.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using Triangle = bvh::Triangle; 17 | using Bvh = bvh::Bvh; 18 | using Ray = bvh::Ray; 19 | 20 | namespace Phasefield { 21 | 22 | struct BVHAdapter::Impl { 23 | Mesh& mesh; 24 | FaceData triangles; 25 | Bvh bvh; 26 | 27 | bool computeIntersection(Vector3 const& p, Vector3 const& dir, Intersection&); 28 | void update(); 29 | 30 | }; 31 | 32 | void BVHAdapter::Impl::update() { 33 | arrayResize(triangles, mesh.faceCount()); 34 | for(Face f : mesh.faces()) { 35 | bvh::Vector3 ps[3]; 36 | HalfEdge he = f.halfEdge(); 37 | size_t i = 0; 38 | for(Vertex v : f.vertices()) { 39 | auto p = v.position(); 40 | ps[i++] = bvh::Vector3{p.x(), p.y(), p.z()}; 41 | } 42 | triangles[f] = Triangle{ps[0], ps[1], ps[2]}; 43 | } 44 | 45 | bvh::SweepSahBuilder builder(bvh); 46 | auto[bboxes, centers] = bvh::compute_bounding_boxes_and_centers(triangles.data(), triangles.size()); 47 | auto global_bbox = bvh::compute_bounding_boxes_union(bboxes.get(), triangles.size()); 48 | builder.build(global_bbox, bboxes.get(), centers.get(), triangles.size()); 49 | } 50 | 51 | 52 | bool BVHAdapter::Impl::computeIntersection(Vector3 const& p, Vector3 const& dir, Intersection& intersection) { 53 | bvh::ClosestPrimitiveIntersector intersector{bvh, triangles.data()}; 54 | bvh::SingleRayTraverser traverser(bvh); 55 | 56 | Ray ray( 57 | {p.x(), p.y(), p.z()}, // origin 58 | {dir.x(), dir.y(), dir.z()}, // direction 59 | 0.0, // minimum distance 60 | 100.0 // maximum distance 61 | ); 62 | auto hit = traverser.traverse(ray, intersector); 63 | if (hit) { 64 | intersection.t = hit->intersection.t; 65 | intersection.u = hit->intersection.t; 66 | intersection.v = hit->intersection.v; 67 | intersection.idx = hit->primitive_index; 68 | return true; 69 | 70 | } else return false; 71 | } 72 | 73 | BVHAdapter::BVHAdapter(Mesh& mesh) : MeshFeature(mesh, false), impl(new Impl{mesh}) { impl->update(); } 74 | 75 | bool BVHAdapter::computeIntersection(Vector3 const& p, Vector3 const& dir, Intersection& intersection) { 76 | return impl->computeIntersection(p, dir, intersection); 77 | } 78 | 79 | void BVHAdapter::update() { 80 | impl->update(); 81 | } 82 | 83 | BVHAdapter::~BVHAdapter() { 84 | delete impl; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Viewer/Bvh.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 10/11/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Surface.h" 8 | #include "MeshFeature.h" 9 | 10 | namespace Phasefield { 11 | 12 | struct BVHAdapter : MeshFeature { 13 | 14 | struct Impl; 15 | Impl* impl; 16 | 17 | explicit BVHAdapter(Mesh& mesh); 18 | ~BVHAdapter(); 19 | 20 | struct Intersection { 21 | float t, u, v; 22 | size_t idx; 23 | }; 24 | 25 | bool computeIntersection(Vector3 const& p, Vector3 const& dir, Intersection& intersection); 26 | 27 | void update() override; 28 | 29 | FEATURE_NAME("BVH") 30 | 31 | }; 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Viewer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | corrade_add_resource(Viewer_Rsc resources.conf) 2 | corrade_add_resource(Experiments_Rsc data/resources.conf) 3 | add_library(Viewer STATIC 4 | Viewer.cpp 5 | Viewer.h 6 | ArcBall.cpp 7 | ArcBall.h 8 | Bvh.cpp 9 | Bvh.h 10 | ${Viewer_Rsc} 11 | ${Experiments_Rsc} 12 | ) 13 | 14 | target_include_directories(Viewer PUBLIC ${CMAKE_CURRENT_LIST_DIR}) 15 | 16 | target_link_libraries(Viewer 17 | PUBLIC 18 | Phasefield::Utilities 19 | ScopedTimer::ScopedTimer 20 | Phasefield::Cost 21 | Phasefield::Optimization 22 | Phasefield::Visualization 23 | Phasefield::VisualizationProxy 24 | 25 | Magnum::GL 26 | Magnum::Magnum 27 | Magnum::MeshTools 28 | Magnum::Application 29 | Magnum::Shaders 30 | Magnum::DebugTools 31 | MagnumIntegration::ImGui 32 | MagnumPlugins::StanfordImporter 33 | MagnumPlugins::StanfordSceneConverter 34 | ImPlot::ImPlot 35 | bvh 36 | ) 37 | 38 | add_library(Phasefield::Viewer ALIAS Viewer) 39 | 40 | if (PHASEFIELD_WITH_VIDEO) 41 | target_link_libraries(Viewer PUBLIC Phasefield::VideoSaver) 42 | target_compile_definitions(Viewer PUBLIC PHASEFIELD_WITH_VIDEO) 43 | endif () 44 | 45 | -------------------------------------------------------------------------------- /Viewer/PlotCallback.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 10/6/20. 3 | // 4 | 5 | #include "PlotCallback.h" 6 | -------------------------------------------------------------------------------- /Viewer/PlotCallback.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 10/6/20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Solver.h" 8 | #include "Tree.h" 9 | #include "RecursiveProblem.h" 10 | 11 | #include 12 | 13 | namespace Phasefield { 14 | 15 | namespace Cr = Corrade; 16 | 17 | //struct PlotCallback { 18 | // 19 | // explicit PlotCallback(Solver::RecursiveProblem& pb, Node n) : problem{pb}, node(n), costs{pb.objectives.size()} {} 20 | // 21 | // Solver::RecursiveProblem& problem; 22 | // Node node; 23 | // Array > costs; 24 | // 25 | // Solver::Status::Value operator()(Solver::IterationSummary const&) { 26 | // for(auto& [f, hist, draw] : problem.objectives) { 27 | // double cost = 0; 28 | // f(node.phasefield(), node.temporary(), cost, nullptr, nullptr); 29 | // arrayAppend(hist, {float(hist.size()), float(cost)}); 30 | // } 31 | // return Solver::Status::CONTINUE; 32 | // } 33 | // 34 | // void generatePlot() { 35 | // std::string plot = "\\begin{tikzpicture}\n" 36 | // "\\begin{axis}[\n" 37 | // "height=9cm,\n" 38 | // "width=9cm,\n" 39 | // "grid=major,\n" 40 | // "]\n"; 41 | // 42 | // size_t sampleCouunt = costs.front().size(); 43 | // size_t step = Math::max(sampleCouunt/50, 1ul); 44 | // for(size_t k = 0; k < costs.size(); ++k) { 45 | // plot += "\n\\addplot coordinates {\n"; 46 | // for(size_t j = 0; j < costs[k].size(); j += step) { 47 | // plot += Cr::Utility::formatString("({},{})\n", j, costs[k][j]); 48 | // } 49 | // plot += "};\n"; 50 | // plot += Cr::Utility::formatString("\\addlegendentry{{ {} }}", f.functionalType)); 51 | // } 52 | // plot += "\n\\end{axis}\n" 53 | // "\\end{tikzpicture}"; 54 | // 55 | // FILE *fp = fopen("/tmp/plot.tex", "w"); 56 | // if (fp != nullptr) { 57 | // fputs(plot.data(), fp); 58 | // fclose(fp); 59 | // } 60 | // } 61 | //}; 62 | 63 | } -------------------------------------------------------------------------------- /Viewer/SourceSansPro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janos95/Phasefields/8d02111d99628222efa7749a91be144a636a3566/Viewer/SourceSansPro-Regular.ttf -------------------------------------------------------------------------------- /Viewer/data/capsule_high_res.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janos95/Phasefields/8d02111d99628222efa7749a91be144a636a3566/Viewer/data/capsule_high_res.ply -------------------------------------------------------------------------------- /Viewer/data/capsule_high_res_split.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janos95/Phasefields/8d02111d99628222efa7749a91be144a636a3566/Viewer/data/capsule_high_res_split.bin -------------------------------------------------------------------------------- /Viewer/data/connectedness.conf: -------------------------------------------------------------------------------- 1 | epsilon=0.04 2 | solver=2 3 | hierarchicalOptimization=false 4 | max_num_iterations=10000 5 | kappa=1 6 | [DoubleWellPotential] 7 | isObjective=true 8 | [DirichletEnergy] 9 | isObjective=true 10 | [ConnectednessConstraint] 11 | weight=0.0005 12 | s=0.7 13 | ignoreSmallComponents=true 14 | isObjective=true 15 | purePhase=1 16 | [AreaRegularizer] 17 | weight=100.0 18 | isObjective=true 19 | -------------------------------------------------------------------------------- /Viewer/data/resources.conf: -------------------------------------------------------------------------------- 1 | group=experiments-data 2 | 3 | [file] 4 | filename=spot.ply 5 | 6 | [file] 7 | filename=spot_high_res.ply 8 | 9 | [file] 10 | filename=capsule_high_res.ply 11 | 12 | [file] 13 | filename=capsule_high_res_split.bin 14 | 15 | [file] 16 | filename=connectedness.conf 17 | 18 | [file] 19 | filename=spot_area.conf 20 | 21 | [file] 22 | filename=sphere.bin 23 | 24 | [file] 25 | filename=sphere_connected.conf 26 | 27 | [file] 28 | filename=sphere.conf 29 | 30 | [file] 31 | filename=sphere.ply 32 | 33 | 34 | -------------------------------------------------------------------------------- /Viewer/data/sphere.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janos95/Phasefields/8d02111d99628222efa7749a91be144a636a3566/Viewer/data/sphere.bin -------------------------------------------------------------------------------- /Viewer/data/sphere.conf: -------------------------------------------------------------------------------- 1 | epsilon=0.089 2 | solver=2 3 | hierarchicalOptimization=false 4 | max_num_iterations=1000 5 | kappa=1 6 | [DirichletEnergy] 7 | isObjective=true 8 | [DoubleWellPotential] 9 | isObjective=true 10 | [DiffuseYamabe] 11 | isObjective=true 12 | lambda=10 -------------------------------------------------------------------------------- /Viewer/data/sphere.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janos95/Phasefields/8d02111d99628222efa7749a91be144a636a3566/Viewer/data/sphere.ply -------------------------------------------------------------------------------- /Viewer/data/sphere_connected.conf: -------------------------------------------------------------------------------- 1 | epsilon=0.089 2 | solver=2 3 | hierarchicalOptimization=false 4 | max_num_iterations=1000 5 | kappa=0.8 6 | [DirichletEnergy] 7 | isObjective=true 8 | [DoubleWellPotential] 9 | isObjective=true 10 | [DiffuseYamabe] 11 | weight=10 12 | lambda=10 13 | isObjective=true 14 | [ConnectednessConstraint] 15 | weight=1 16 | s=0.22795 17 | ignoreSmallComponents=false 18 | isObjective=true 19 | purePhase=-1 20 | [ConnectednessConstraint] 21 | weight=1 22 | s=0.22795 23 | ignoreSmallComponents=false 24 | isObjective=true 25 | purePhase=1 26 | -------------------------------------------------------------------------------- /Viewer/data/spot.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janos95/Phasefields/8d02111d99628222efa7749a91be144a636a3566/Viewer/data/spot.ply -------------------------------------------------------------------------------- /Viewer/data/spot_area.conf: -------------------------------------------------------------------------------- 1 | epsilon=0.015 2 | solver=2 3 | hierarchicalOptimization=false 4 | max_num_iterations=5000 5 | kappa=1 6 | [DirichletEnergy] 7 | isObjective=true 8 | [DoubleWellPotential] 9 | isObjective=true 10 | [AreaRegularizer] 11 | isObjective=true 12 | weight=100.0 13 | -------------------------------------------------------------------------------- /Viewer/data/spot_high_res.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janos95/Phasefields/8d02111d99628222efa7749a91be144a636a3566/Viewer/data/spot_high_res.ply -------------------------------------------------------------------------------- /Viewer/resources.conf: -------------------------------------------------------------------------------- 1 | group=viewer-data 2 | 3 | [file] 4 | filename=SourceSansPro-Regular.ttf 5 | 6 | -------------------------------------------------------------------------------- /Visualization/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(Visualization STATIC 2 | ImGuiWidgets.h 3 | ImGuiWidget.cpp 4 | Visualization.h 5 | ) 6 | target_include_directories(Visualization PUBLIC ${CMAKE_CURRENT_LIST_DIR}) 7 | target_link_libraries(Visualization 8 | PUBLIC 9 | Magnum::GL 10 | Magnum::Magnum 11 | Magnum::MeshTools 12 | MagnumIntegration::ImGui 13 | Phasefield::Utilities 14 | ) 15 | set_property(TARGET Visualization PROPERTY POSITION_INDEPENDENT_CODE ON) 16 | add_library(Phasefield::Visualization ALIAS Visualization) 17 | 18 | 19 | add_library(VisualizationProxy STATIC 20 | VisualizationProxy.cpp 21 | VisualizationProxy.h 22 | ) 23 | 24 | target_include_directories(VisualizationProxy PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 25 | target_link_libraries(VisualizationProxy 26 | PRIVATE 27 | Magnum::GL 28 | Magnum::Magnum 29 | Phasefield::Viewer 30 | PUBLIC 31 | Phasefield::Utilities 32 | ) 33 | 34 | set_property(TARGET VisualizationProxy PROPERTY POSITION_INDEPENDENT_CODE ON) 35 | add_library(Phasefield::VisualizationProxy ALIAS VisualizationProxy) 36 | -------------------------------------------------------------------------------- /Visualization/ImGuiWidget.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 08.04.20. 3 | // taken from some imgui github issue 4 | // 5 | 6 | #include "ImGuiWidgets.h" 7 | 8 | #include 9 | #include 10 | 11 | bool toggleButton(const char* str_id, bool* v) 12 | { 13 | 14 | bool clicked = false; 15 | ImVec2 p = ImGui::GetCursorScreenPos(); 16 | ImDrawList* draw_list = ImGui::GetWindowDrawList(); 17 | 18 | float height = ImGui::GetFrameHeight(); 19 | float width = height * 1.55f; 20 | float radius = height * 0.50f; 21 | 22 | ImGui::InvisibleButton(str_id, ImVec2(width, height)); 23 | if (ImGui::IsItemClicked()){ 24 | clicked = true; 25 | *v = !*v; 26 | } 27 | 28 | float t = *v ? 1.0f : 0.0f; 29 | 30 | ImGuiContext& g = *GImGui; 31 | float ANIM_SPEED = 0.08f; 32 | if (g.LastActiveId == g.CurrentWindow->GetID(str_id))// && g.LastActiveIdTimer < ANIM_SPEED) 33 | { 34 | float t_anim = ImSaturate(g.LastActiveIdTimer / ANIM_SPEED); 35 | t = *v ? (t_anim) : (1.0f - t_anim); 36 | } 37 | 38 | ImU32 col_bg; 39 | if (ImGui::IsItemHovered()) 40 | col_bg = ImGui::GetColorU32(ImLerp(ImVec4(0.78f, 0.78f, 0.78f, 1.0f), ImVec4(0.64f, 0.83f, 0.34f, 1.0f), t)); 41 | else 42 | col_bg = ImGui::GetColorU32(ImLerp(ImVec4(0.85f, 0.85f, 0.85f, 1.0f), ImVec4(0.56f, 0.83f, 0.26f, 1.0f), t)); 43 | 44 | draw_list->AddRectFilled(p, ImVec2(p.x + width, p.y + height), col_bg, height * 0.5f); 45 | draw_list->AddCircleFilled(ImVec2(p.x + radius + t * (width - radius * 2.0f), p.y + radius), radius - 1.5f, IM_COL32(255, 255, 255, 255)); 46 | 47 | return clicked; 48 | } 49 | 50 | bool dragDoubleRange2(const char* label, double* v_current_min, double* v_current_max, float v_speed, double v_min, double v_max, const char* format, const char* format_max, float power) 51 | { 52 | ImGuiWindow* window = ImGui::GetCurrentWindow(); 53 | if (window->SkipItems) 54 | return false; 55 | 56 | ImGuiContext& g = *GImGui; 57 | ImGui::PushID(label); 58 | ImGui::BeginGroup(); 59 | ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); 60 | 61 | auto min = (v_min >= v_max) ? -DBL_MAX : v_min; 62 | auto max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max); 63 | bool value_changed = ImGui::DragScalar("##min", ImGuiDataType_Double, v_current_min, v_speed, &min, &max, format, power); 64 | ImGui::PopItemWidth(); 65 | ImGui::SameLine(0, g.Style.ItemInnerSpacing.x); 66 | min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min); 67 | max = (v_min >= v_max) ? FLT_MAX : v_max; 68 | value_changed |= ImGui::DragScalar("##max", ImGuiDataType_Double, v_current_max, v_speed, &min, &max, format_max ? format_max : format, power); 69 | ImGui::PopItemWidth(); 70 | ImGui::SameLine(0, g.Style.ItemInnerSpacing.x); 71 | 72 | ImGui::TextEx(label, ImGui::FindRenderedTextEnd(label)); 73 | ImGui::EndGroup(); 74 | ImGui::PopID(); 75 | return value_changed; 76 | } 77 | 78 | //ImRect RenderTree(Node* n) 79 | //{ 80 | // const bool recurse = ImGui::TreeNode(...); 81 | // const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); 82 | // 83 | // if (recurse) 84 | // { 85 | // const ImColor TreeLineColor = ImGui::GetColorU32(ImGuiCol_Text); 86 | // const float SmallOffsetX = 11.0f; //for now, a hardcoded value; should take into account tree indent size 87 | // ImDrawList* drawList = ImGui::GetWindowDrawList(); 88 | // 89 | // ImVec2 verticalLineStart = ImGui::GetCursorScreenPos(); 90 | // verticalLineStart.x += SmallOffsetX; //to nicely line up with the arrow symbol 91 | // ImVec2 verticalLineEnd = verticalLineStart; 92 | // 93 | // for (Node* child : *n) 94 | // { 95 | // const float HorizontalTreeLineSize = 8.0f; //chosen arbitrarily 96 | // const ImRect childRect = RenderTree(child); 97 | // const float midpoint = (childRect.Min.y + childRect.Max.y) / 2.0f; 98 | // drawList->AddLine(ImVec2(verticalLineStart.x, midpoint), ImVec(verticalLineStart.x + HorizontalTreeLineSize, midpoint), TreeLineColor); 99 | // verticalLineEnd.y = midpoint; 100 | // } 101 | // 102 | // drawList->AddLine(verticalLineStart, verticalLineEnd, TreeLineColor); 103 | // } 104 | // 105 | // return nodeRect; 106 | //} 107 | -------------------------------------------------------------------------------- /Visualization/ImGuiWidgets.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 08.04.20. 3 | // 4 | 5 | #pragma once 6 | 7 | 8 | bool toggleButton(const char* str_id, bool* v); 9 | bool dragDoubleRange2(const char* label, double* v_current_min, double* v_current_max, float v_speed, double v_min, double v_max, const char* format, const char* format_max, float power); 10 | -------------------------------------------------------------------------------- /Visualization/Paths.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 08.05.20. 3 | // 4 | 5 | #include "Paths.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | Paths::Paths(): 14 | shader{Mg::Shaders::Phong::Flag::VertexColor|Mg::Shaders::Phong::Flag::InstancedTransformation}, 15 | cylinder{Mg::MeshTools::compile(Mg::Primitives::cylinderSolid(3,5,0.5f))} 16 | { 17 | shader.setAmbientColor(0x111111_rgbf) 18 | .setSpecularColor(0x330000_rgbf) 19 | .setLightPosition({10.0f, 15.0f, 5.0f}); 20 | 21 | /* cylinder mesh, with an (initially empty) instance buffer */ 22 | cylinder.addVertexBufferInstanced(instanceBuffer, 1, 0, 23 | Mg::Shaders::Phong::TransformationMatrix{}, 24 | Mg::Shaders::Phong::NormalMatrix{}, 25 | Mg::Shaders::Phong::Color3{}); 26 | 27 | } 28 | 29 | void Paths::draw(const Mg::Matrix4& transformation, Mg::Matrix4 const& projection){ 30 | if(!drawPaths || instanceData.empty()) return; 31 | Mg::Containers::arrayResize(instanceDataTransformed, Mg::Containers::NoInit, instanceData.size()); 32 | 33 | for (int i = 0; i < instanceData.size(); ++i) { 34 | instanceDataTransformed[i].normalMatrix = transformation.normalMatrix() * instanceData[i].normalMatrix; 35 | instanceDataTransformed[i].tf = transformation * instanceData[i].tf; 36 | instanceDataTransformed[i].color = instanceData[i].color; 37 | } 38 | 39 | instanceBuffer.setData(instanceDataTransformed, Mg::GL::BufferUsage::DynamicDraw); 40 | cylinder.setInstanceCount(instanceDataTransformed.size()); 41 | shader.setProjectionMatrix(projection) 42 | .draw(cylinder); 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /Visualization/Paths.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 08.05.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Enums.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | 20 | namespace Mg = Magnum; 21 | namespace Cr = Corrade; 22 | 23 | using namespace Mg::Math::Literals; 24 | 25 | struct InstanceData { 26 | Mg::Matrix4 tf; 27 | Mg::Matrix3 normalMatrix; 28 | Mg::Color3 color; 29 | }; 30 | 31 | /** 32 | * @todo Instead of instancing a better options would be to simply upload 33 | * @todo all edges as a line set and write a custom shader which for each 34 | * @todo edge generates a sreen aligned quad in a geometry shader and some normals 35 | * @todo so that we can than add some plasticity. For junctions either use blending 36 | * @todo or compute an sdf and use a second render pass to decide which normals 37 | * @todo to use for shading. 38 | */ 39 | 40 | struct Paths { 41 | explicit Paths(); 42 | void draw(Mg::Matrix4 const& transformation, Mg::Matrix4 const& projection); 43 | 44 | Mg::Shaders::Phong shader; 45 | Mg::GL::Mesh cylinder; 46 | Mg::GL::Buffer instanceBuffer; 47 | Mg::Containers::Array instanceData; 48 | Mg::Containers::Array instanceDataTransformed; 49 | bool drawPaths = false; 50 | }; 51 | 52 | 53 | -------------------------------------------------------------------------------- /Visualization/Upload.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 13.03.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace Mg = Magnum; 13 | namespace Cr = Corrade; 14 | 15 | enum class CompileFlag : Mg::UnsignedInt { 16 | GenerateFlatNormals = 1u << 0u, 17 | GenerateSmoothNormals = 1u << 1u, 18 | AddColorAttribute = 1u << 2u, 19 | AddNormalAttribute = 1u << 3u, 20 | AddTextureCoordinates = 1u << 4u, 21 | }; 22 | 23 | using CompileFlags = Cr::Containers::EnumSet; 24 | 25 | CORRADE_ENUMSET_OPERATORS(CompileFlags) 26 | 27 | Mg::Trade::MeshData preprocess( 28 | Cr::Containers::ArrayView const& vertices, 29 | Cr::Containers::ArrayView const& indices, 30 | CompileFlags flags = {}); 31 | 32 | void upload(Mg::GL::Mesh& mesh, Mg::GL::Buffer& vertices, Mg::GL::Buffer& indices, Mg::Trade::MeshData& meshData); 33 | 34 | void reuploadVertices(Mg::GL::Buffer& vertices, Mg::Trade::MeshData const& meshData); 35 | 36 | -------------------------------------------------------------------------------- /Visualization/Visualization.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 9/11/20. 3 | // 4 | 5 | #pragma once 6 | 7 | /* forward declarations */ 8 | 9 | namespace Phasefield { 10 | 11 | struct VisualizationProxy; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Visualization/VisualizationProxy.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 09.06.20. 3 | // 4 | 5 | #include "VisualizationProxy.h" 6 | #include "Viewer.h" 7 | #include "Enums.h" 8 | #include "C1Functions.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | namespace Phasefield { 22 | 23 | using namespace Mg::Math::Literals; 24 | 25 | const Color4 kelly_colors[] = { 26 | 0xFFFFB300_rgbf, //Vivid Yellow 27 | 0xFF803E75_rgbf, //Strong Purple 28 | 0xFFFF6800_rgbf, //Vivid Orange 29 | 0xFFA6BDD7_rgbf, //Very Light Blue 30 | 0xFFC10020_rgbf, //Vivid Red 31 | 0xFFCEA262_rgbf, //Grayish Yellow 32 | 0xFF817066_rgbf, //Medium Gray 33 | 0xFF007D34_rgbf, //Vivid Green 34 | 0xFFF6768E_rgbf, //Strong Purplish Pink 35 | 0xFF00538A_rgbf, //Strong Blue 36 | 0xFFFF7A5C_rgbf, //Strong Yellowish Pink 37 | 0xFF53377A_rgbf, //Strong Violet 38 | 0xFFFF8E00_rgbf, //Vivid Orange Yellow 39 | 0xFFB32851_rgbf, //Strong Purplish Red 40 | 0xFFF4C800_rgbf, //Vivid Greenish Yellow 41 | 0xFF7F180D_rgbf, //Strong Reddish Brown 42 | 0xFF93AA00_rgbf, //Vivid Yellowish Green 43 | 0xFF593315_rgbf, //Deep Yellowish Brown 44 | 0xFFF13A13_rgbf, //Vivid Reddish Orange 45 | 0xFF232C16_rgbf //Dark Olive Green 46 | }; 47 | 48 | static Array g_colors; 49 | 50 | #define USE_KELLY 0 51 | 52 | Array& getColors(size_t n) { 53 | if(n == Invalid) { 54 | return g_colors; 55 | } 56 | if(n != g_colors.size()) { 57 | arrayResize(g_colors, n); 58 | if(n > 20 || !USE_KELLY) { 59 | Deg hue = 100.0_degf; 60 | Deg step = 360._degf/n; 61 | for(size_t i = 0; i < n; ++i) { 62 | g_colors[i] = Color4::fromHsv({hue += step, 0.9f, 0.7f}); 63 | } 64 | } else { 65 | for(size_t i = 0; i < n; ++i) { 66 | g_colors[i] = kelly_colors[i]; 67 | } 68 | } 69 | } 70 | return g_colors; 71 | } 72 | 73 | void optimizeColors(Array const& neighbors, Array const& starts) { 74 | size_t segmentCount = starts.size() - 1; 75 | auto& colors = getColors(segmentCount); 76 | 77 | std::random_device device; 78 | std::mt19937 gen(device()); 79 | std::uniform_int_distribution idxDist(0, segmentCount - 1); 80 | 81 | for(size_t i = 0; i < 1'000'000; ++i) { 82 | 83 | size_t indices[2] = {idxDist(gen), idxDist(gen)}; 84 | float costOrig = 0; 85 | float costSwapped = 0; 86 | 87 | for(size_t l = 0; l < 2; ++l) { 88 | size_t idx = indices[l]; 89 | Color4& colorOrig = colors[indices[l]]; 90 | Color4& colorSwapped = colors[indices[l^1]]; 91 | for(size_t j = starts[idx]; j < starts[idx+1]; ++j) { 92 | size_t k = neighbors[j]; 93 | CORRADE_ASSERT(k < colors.size(), "Color Opt: Index out of bounds",); 94 | costOrig += (colorOrig - colors[k]).dot(); 95 | costSwapped += (colorSwapped - colors[k]).dot(); 96 | } 97 | } 98 | 99 | if(costSwapped > costOrig) { 100 | std::swap(colors[indices[0]], colors[indices[1]]); 101 | } 102 | } 103 | } 104 | 105 | VisualizationProxy::VisualizationProxy(Viewer& v) : viewer(v) { 106 | setDefaultCallback(); 107 | } 108 | 109 | //void VisualizationProxy::setFaceColors(Containers::ArrayView& data){ 110 | // std::lock_guard l(mutex); 111 | //} 112 | // 113 | //void setFaceColors(Containers::ArrayView& data){ 114 | // 115 | //} 116 | 117 | 118 | void VisualizationProxy::upload() { 119 | if(updateVertexBuffer) { 120 | viewer.mesh.uploadVertexBuffer(viewer.vertexBuffer); 121 | updateVertexBuffer = false; 122 | } 123 | } 124 | 125 | void VisualizationProxy::drawSegmentation() { 126 | 127 | Tree& tree = viewer.tree; 128 | 129 | auto& colors = getColors(tree.nodeCountOnLevel(level)*2); 130 | auto vertexColors = viewer.mesh.colors(); 131 | 132 | for(Color4& c : vertexColors) c = Color4{}; 133 | 134 | size_t n = tree.vertexCount(); 135 | SmootherStep smoothStep; 136 | 137 | tree.computeWeightsOfAncestorsOfLevel(level); 138 | 139 | size_t idx = 0; 140 | for(Node node : tree.nodesOnLevel(level)) { 141 | for(size_t i = 0; i < n; ++i) { 142 | vertexColors[i].rgb() += node.temporary()[i]*smoothStep.eval(node.phasefield()[i])*colors[2*idx].rgb(); 143 | vertexColors[i].rgb() += node.temporary()[i]*smoothStep.eval(-node.phasefield()[i])*colors[2*idx + 1].rgb(); 144 | } 145 | ++idx; 146 | } 147 | shaderConfig = ShaderConfig::VertexColors; 148 | } 149 | 150 | void VisualizationProxy::redraw() { 151 | drawCb(viewer.currentNode); 152 | viewer.redraw(); 153 | updateVertexBuffer = true; 154 | } 155 | 156 | void VisualizationProxy::setDefaultCallback() { 157 | switch(option) { 158 | case VisOption::Segmentation : 159 | drawCb = [this](Node) { drawSegmentation(); }; 160 | break; 161 | case VisOption::Phasefield : 162 | scale = 0.5; 163 | offset = 1; 164 | drawCb = [this](Node node) { drawValues(node.phasefield()); }; 165 | break; 166 | case VisOption::Weight : 167 | scale = 1; 168 | offset = 0; 169 | drawCb = [this](Node node) { drawValues(node.temporary()); }; 170 | break; 171 | } 172 | releaseCb = []{}; 173 | } 174 | 175 | void VisualizationProxy::setCallbacks(UniqueFunction draw, UniqueFunction release) { 176 | releaseCb(); 177 | drawCb = std::move(draw); 178 | releaseCb = std::move(release); 179 | } 180 | 181 | void VisualizationProxy::drawValues(VertexDataView const& values) { 182 | CORRADE_INTERNAL_ASSERT(values.size() == viewer.mesh.vertexCount()); 183 | for(Vertex v : viewer.mesh.vertices()) { 184 | viewer.mesh.scalar(v) = scale*(values[v] + offset); 185 | } 186 | shaderConfig = ShaderConfig::ColorMaps; 187 | } 188 | 189 | void VisualizationProxy::drawValuesNormalized(VertexDataView const& values) { 190 | if(!customMapping) { 191 | double min, max, w; 192 | std::tie(min, max) = Math::minmax(ArrayView(values)); 193 | if(max - min < 1e-10) 194 | w = 1; 195 | else 196 | w = max - min; 197 | Debug{} << "Min" << min << ", Max" << max; 198 | scale = 1/w; 199 | offset = -min; 200 | } 201 | drawValues(values); 202 | } 203 | 204 | } -------------------------------------------------------------------------------- /Visualization/VisualizationProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 09.06.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "Enums.h" 8 | #include "UniqueFunction.h" 9 | #include "Surface.h" 10 | 11 | 12 | //TODO UniqueFunction blacks out with reference to incomplete type ?? 13 | #include "Tree.h" 14 | 15 | namespace Phasefield { 16 | 17 | SMART_ENUM(VisOption, size_t, 18 | Phasefield, 19 | Weight, 20 | Segmentation) 21 | 22 | /** 23 | * @param n numbers of colors you want 24 | * @return array of hopefully visually distinct colors 25 | */ 26 | Array& getColors(size_t n = Invalid); 27 | 28 | void optimizeColors(Array const& neighbors, Array const& starts); 29 | 30 | /** 31 | * The main purpose of this class is to avoid including the whole Viewer.h 32 | * everywhere we want to manipulate some of the drawing state. 33 | */ 34 | struct VisualizationProxy { 35 | 36 | enum class ShaderConfig { 37 | VertexColors, 38 | ColorMaps 39 | }; 40 | 41 | ShaderConfig shaderConfig = ShaderConfig::ColorMaps; 42 | 43 | VisualizationProxy(class Viewer&); 44 | 45 | //void setFaceColors(Containers::ArrayView& data); 46 | //void setFaceColors(Containers::ArrayView& data); 47 | 48 | void redraw(); 49 | 50 | void upload(); 51 | 52 | bool updateVertexBuffer = false; 53 | 54 | //Containers::Array faceColors; 55 | Viewer& viewer; 56 | 57 | void drawSegmentation(); 58 | 59 | void drawValues(VertexDataView const& values); 60 | 61 | void drawValuesNormalized(VertexDataView const& values); 62 | 63 | void setDefaultCallback(); 64 | 65 | void setCallbacks(UniqueFunction, UniqueFunction); 66 | 67 | UniqueFunction drawCb; 68 | UniqueFunction releaseCb; 69 | 70 | VisOption::Value option = VisOption::Phasefield; 71 | 72 | bool customMapping = false; 73 | double scale = 1; 74 | double offset = 0; 75 | int level = 0; 76 | }; 77 | 78 | } -------------------------------------------------------------------------------- /Visualization/primitive_options.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 18.05.20. 3 | // 4 | 5 | #include "primitive_options.hpp" 6 | #include 7 | 8 | bool PolygonizationOptions::drawImGui() { 9 | constexpr static float minAngle = 10.f, maxAngle = 60.f; 10 | constexpr static float minBoundingRadius = 1., maxBoundingRadius = 10.f; 11 | constexpr static float minDistanceBound = 0.001f, maxDistanceBound = .5f; 12 | constexpr static float minRadiusBound = 0.001f, maxRadiusBound = .5f; 13 | bool refresh = false; 14 | refresh |= ImGui::SliderScalar("Angle Bound", ImGuiDataType_Float, &angleBound, &minAngle, &maxAngle, "%f"); 15 | refresh |= ImGui::SliderScalar("Bounding Sphere Radius", ImGuiDataType_Float, &boundingSphereRadius, &minBoundingRadius, &maxBoundingRadius, "%f"); 16 | refresh |= ImGui::SliderScalar("Distance Bound", ImGuiDataType_Float, &distanceBound, &minDistanceBound, &maxDistanceBound, "%.2e"); 17 | refresh |= ImGui::SliderScalar("Radius Bound", ImGuiDataType_Float, &radiusBound, &minRadiusBound, &maxRadiusBound, "%.2e"); 18 | return refresh; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Visualization/primitive_options.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by janos on 29.04.20. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | struct AbstractPrimitiveOptions { 10 | virtual ~AbstractPrimitiveOptions() = default; 11 | virtual bool drawImGui() { return false; }; 12 | }; 13 | 14 | struct CapsuleOptions : AbstractPrimitiveOptions { 15 | std::uint32_t hemisphereRings = 10; // Number of (face) rings for each hemisphere. Must be larger or equal to 1. 16 | std::uint32_t cylinderRings = 30; // Number of (face) rings for cylinder. Must be larger or equal to 1. 17 | std::uint32_t segments = 30; // Number of (face) segments. Must be larger or equal to 3. 18 | float radius = 1.f; 19 | float length = 5.f; 20 | }; 21 | 22 | struct UOptions : AbstractPrimitiveOptions { 23 | float height = 1.f; 24 | float width = 1.f; 25 | float innerWidth = .5f; 26 | float innerHeight = .5f; 27 | }; 28 | 29 | struct PolygonizationOptions : AbstractPrimitiveOptions { 30 | float angleBound = 30.f; 31 | float radiusBound = 0.1f; 32 | float distanceBound = 0.1f; 33 | float boundingSphereRadius = 2.f; 34 | 35 | bool drawImGui() override; 36 | }; 37 | 38 | struct WireframeOptions : PolygonizationOptions { 39 | double thickness = 0.001; 40 | }; 41 | -------------------------------------------------------------------------------- /conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | ipopt/3.13.2 3 | 4 | 5 | [generators] 6 | cmake_find_package 7 | 8 | -------------------------------------------------------------------------------- /contrib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Corrade 2 | set(BUILD_PLUGINS_STATIC ON CACHE BOOL "" FORCE) 3 | set(BUILD_STATIC ON CACHE BOOL "" FORCE) 4 | set(WITH_CONTAINERS ON CACHE BOOL "" FORCE) 5 | set(WITH_UTILITY ON CACHE BOOL "" FORCE) 6 | 7 | add_subdirectory(corrade EXCLUDE_FROM_ALL) 8 | find_package(Corrade) # bring in CORRADE_TARGET_* macros 9 | 10 | # Magnum 11 | set(WITH_ANYIMAGEIMPORTER ON CACHE BOOL "" FORCE) 12 | set(WITH_STANFORDSCENECONVERTER ON CACHE BOOL "" FORCE) 13 | set(WITH_STANFORDIMPORTER ON CACHE BOOL "" FORCE) 14 | 15 | if (CORRADE_TARGET_EMSCRIPTEN) 16 | set(WITH_EMSCRIPTENAPPLICATION ON CACHE BOOL "" FORCE) 17 | else() 18 | # GLFW 19 | set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 20 | set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) 21 | set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) 22 | set(GLFW_INSTALL OFF CACHE BOOL "" FORCE) 23 | add_subdirectory(glfw EXCLUDE_FROM_ALL) 24 | 25 | set(WITH_GLFWAPPLICATION ON CACHE BOOL "" FORCE) 26 | endif () 27 | 28 | add_subdirectory(magnum EXCLUDE_FROM_ALL) 29 | add_subdirectory(magnum-plugins EXCLUDE_FROM_ALL) 30 | 31 | # Magnum-Integration 32 | set(IMGUI_DIR "${CMAKE_CURRENT_SOURCE_DIR}/imgui") 33 | set(WITH_IMGUI ON CACHE BOOL "" FORCE) 34 | add_subdirectory(magnum-integration EXCLUDE_FROM_ALL) 35 | find_package(MagnumIntegration REQUIRED ImGui) 36 | 37 | # ImPlot 38 | add_library(implot implot/implot.h implot/implot_internal.h implot/implot.cpp implot/implot_items.cpp) 39 | target_include_directories(implot PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/implot) 40 | target_link_libraries(implot PUBLIC MagnumIntegration::ImGui) 41 | add_library(ImPlot::ImPlot ALIAS implot) 42 | 43 | # lbfgs 44 | add_subdirectory(liblbfgs EXCLUDE_FROM_ALL) 45 | 46 | # Bounding Volume Hierarchy 47 | add_subdirectory(bvh EXCLUDE_FROM_ALL) 48 | 49 | # Eigen 50 | add_library(Eigen INTERFACE) 51 | target_include_directories(Eigen INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/eigen/) 52 | add_library(Eigen3::Eigen ALIAS Eigen) 53 | 54 | set(WITH_DEMOS OFF CACHE BOOL "" FORCE) 55 | add_subdirectory(SuiteSparse) 56 | 57 | -------------------------------------------------------------------------------- /images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janos95/Phasefields/8d02111d99628222efa7749a91be144a636a3566/images/image.png -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Viewer.h" 3 | #include "ScopedTimer/ScopedTimer.h" 4 | 5 | 6 | #ifndef MAGNUM_TARGET_WEBGL 7 | int main(int argc, char** argv){ 8 | 9 | Phasefield::Viewer* viewer; 10 | { 11 | ScopedTimer t{"Application Start", true}; 12 | viewer = new Phasefield::Viewer{{argc, argv}}; 13 | } 14 | 15 | while(viewer->mainLoopIteration()) { 16 | if(viewer->isOptimizing) { 17 | viewer->runOptimization([&viewer]{ return viewer->mainLoopIteration(); }); 18 | viewer->isOptimizing = false; 19 | } 20 | } 21 | } 22 | #else 23 | MAGNUM_APPLICATION_MAIN(Phasefield::Viewer) 24 | #endif -------------------------------------------------------------------------------- /modules/FindAssimp.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # Find Assimp 3 | # ------------- 4 | # 5 | # Finds the Assimp library. This module defines: 6 | # 7 | # Assimp_FOUND - True if Assimp library is found 8 | # Assimp::Assimp - Assimp imported target 9 | # 10 | # Additionally these variables are defined for internal usage: 11 | # 12 | # ASSIMP_LIBRARY - Assimp library 13 | # ASSIMP_INCLUDE_DIR - Include dir 14 | # 15 | 16 | # 17 | # This file is part of Magnum. 18 | # 19 | # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 20 | # Vladimír Vondruš 21 | # Copyright © 2017 Jonathan Hale 22 | # 23 | # Permission is hereby granted, free of charge, to any person obtaining a 24 | # copy of this software and associated documentation files (the "Software"), 25 | # to deal in the Software without restriction, including without limitation 26 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 27 | # and/or sell copies of the Software, and to permit persons to whom the 28 | # Software is furnished to do so, subject to the following conditions: 29 | # 30 | # The above copyright notice and this permission notice shall be included 31 | # in all copies or substantial portions of the Software. 32 | # 33 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 36 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 38 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 39 | # DEALINGS IN THE SOFTWARE. 40 | # 41 | 42 | find_path(ASSIMP_INCLUDE_DIR NAMES assimp/anim.h HINTS include) 43 | 44 | if (WIN32 AND MSVC) 45 | # Adapted from https://github.com/assimp/assimp/blob/799fd74714f9ffac29004c6b5a674b3402524094/CMakeLists.txt#L645-L655 46 | # with versions below MSVC 2015 (14.0 / 1900) removed, and the discouraged 47 | # use of MSVCxy replaced with MSVC_VERSION. See also 48 | # https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering 49 | if (MSVC_TOOLSET_VERSION) # available only since CMake 3.12 50 | set(ASSIMP_MSVC_VERSION vc${MSVC_TOOLSET_VERSION}) 51 | elseif (MSVC_VERSION VERSION_LESS 1910) 52 | set(ASSIMP_MSVC_VERSION vc140) 53 | elseif (MSVC_VERSION VERSION_LESS 1920) 54 | set(ASSIMP_MSVC_VERSION vc141) 55 | elseif (MSVC_VERSION VERSION_LESS 1930) 56 | set(ASSIMP_MSVC_VERSION vc142) 57 | else () 58 | message(FATAL_ERROR "Unsupported MSVC version") 59 | endif () 60 | 61 | if (CMAKE_SIZEOF_VOID_P EQUAL 8) 62 | set(ASSIMP_LIBRARY_DIR "lib64") 63 | elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) 64 | set(ASSIMP_LIBRARY_DIR "lib32") 65 | endif () 66 | 67 | find_library(ASSIMP_LIBRARY_RELEASE assimp-${ASSIMP_MSVC_VERSION}-mt.lib PATHS ${ASSIMP_LIBRARY_DIR}) 68 | find_library(ASSIMP_LIBRARY_DEBUG assimp-${ASSIMP_MSVC_VERSION}-mtd.lib PATHS ${ASSIMP_LIBRARY_DIR}) 69 | else () 70 | find_library(ASSIMP_LIBRARY_RELEASE assimp) 71 | find_library(ASSIMP_LIBRARY_DEBUG assimpd) 72 | endif () 73 | 74 | # Static build of Assimp (built with Vcpkg, any system) depends on IrrXML, find 75 | # that one as well. If not found, simply don't link to it --- it might be a 76 | # dynamic build (on Windows it's a *.lib either way), or a static build using 77 | # system IrrXML. Related issue: https://github.com/Microsoft/vcpkg/issues/5012 78 | if (ASSIMP_LIBRARY_DEBUG MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$" OR ASSIMP_LIBRARY_RELEASE MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$") 79 | find_library(ASSIMP_IRRXML_LIBRARY_RELEASE IrrXML) 80 | find_library(ASSIMP_IRRXML_LIBRARY_DEBUG IrrXMLd) 81 | endif () 82 | 83 | include(SelectLibraryConfigurations) 84 | select_library_configurations(ASSIMP) 85 | 86 | include(FindPackageHandleStandardArgs) 87 | find_package_handle_standard_args(Assimp DEFAULT_MSG 88 | ASSIMP_LIBRARY 89 | ASSIMP_INCLUDE_DIR) 90 | 91 | if (NOT TARGET Assimp::Assimp) 92 | add_library(Assimp::Assimp UNKNOWN IMPORTED) 93 | 94 | if (ASSIMP_LIBRARY_DEBUG AND ASSIMP_LIBRARY_RELEASE) 95 | set_target_properties(Assimp::Assimp PROPERTIES 96 | IMPORTED_LOCATION_DEBUG ${ASSIMP_LIBRARY_DEBUG} 97 | IMPORTED_LOCATION_RELEASE ${ASSIMP_LIBRARY_RELEASE}) 98 | else () 99 | set_target_properties(Assimp::Assimp PROPERTIES 100 | IMPORTED_LOCATION ${ASSIMP_LIBRARY}) 101 | endif () 102 | 103 | # Link to IrrXML as well, if found. See the comment above for details. 104 | if (ASSIMP_IRRXML_LIBRARY_RELEASE) 105 | set_property(TARGET Assimp::Assimp APPEND PROPERTY 106 | INTERFACE_LINK_LIBRARIES $<$>:${ASSIMP_IRRXML_LIBRARY_RELEASE}>) 107 | endif () 108 | if (ASSIMP_IRRXML_LIBRARY_DEBUG) 109 | set_property(TARGET Assimp::Assimp APPEND PROPERTY 110 | INTERFACE_LINK_LIBRARIES $<$:${ASSIMP_IRRXML_LIBRARY_DEBUG}>) 111 | endif () 112 | 113 | set_target_properties(Assimp::Assimp PROPERTIES 114 | INTERFACE_INCLUDE_DIRECTORIES ${ASSIMP_INCLUDE_DIR}) 115 | endif () 116 | -------------------------------------------------------------------------------- /modules/FindOpenGLES2.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # Find OpenGL ES 2 3 | # ---------------- 4 | # 5 | # Finds the OpenGL ES 2 library. This module defines: 6 | # 7 | # OpenGLES2_FOUND - True if OpenGL ES 2 library is found 8 | # OpenGLES2::OpenGLES2 - OpenGL ES 2 imported target 9 | # 10 | # Additionally these variables are defined for internal usage: 11 | # 12 | # OPENGLES2_LIBRARY - OpenGL ES 2 library 13 | # 14 | # Please note this find module is tailored especially for the needs of Magnum. 15 | # In particular, it depends on its platform definitions and doesn't look for 16 | # OpenGL ES includes as Magnum has its own, generated using flextGL. 17 | # 18 | 19 | # 20 | # This file is part of Magnum. 21 | # 22 | # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 23 | # 2020 Vladimír Vondruš 24 | # 25 | # Permission is hereby granted, free of charge, to any person obtaining a 26 | # copy of this software and associated documentation files (the "Software"), 27 | # to deal in the Software without restriction, including without limitation 28 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 29 | # and/or sell copies of the Software, and to permit persons to whom the 30 | # Software is furnished to do so, subject to the following conditions: 31 | # 32 | # The above copyright notice and this permission notice shall be included 33 | # in all copies or substantial portions of the Software. 34 | # 35 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 38 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 40 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 41 | # DEALINGS IN THE SOFTWARE. 42 | # 43 | 44 | # Under Emscripten, GL is linked implicitly. With MINIMAL_RUNTIME you need to 45 | # specify -lGL. Simply set the library name to that. 46 | if(CORRADE_TARGET_EMSCRIPTEN) 47 | set(OPENGLES2_LIBRARY GL CACHE STRING "Path to a library." FORCE) 48 | else() 49 | find_library(OPENGLES2_LIBRARY NAMES 50 | GLESv2 51 | 52 | # ANGLE (CMake doesn't search for lib prefix on Windows) 53 | libGLESv2 54 | 55 | # iOS 56 | OpenGLES) 57 | endif() 58 | 59 | include(FindPackageHandleStandardArgs) 60 | find_package_handle_standard_args(OpenGLES2 DEFAULT_MSG 61 | OPENGLES2_LIBRARY) 62 | 63 | if(NOT TARGET OpenGLES2::OpenGLES2) 64 | # Work around BUGGY framework support on macOS. Do this also in case of 65 | # Emscripten, since there we don't have a location either. 66 | # http://public.kitware.com/pipermail/cmake/2016-April/063179.html 67 | if((CORRADE_TARGET_APPLE AND ${OPENGLES2_LIBRARY} MATCHES "\\.framework$") OR CORRADE_TARGET_EMSCRIPTEN) 68 | add_library(OpenGLES2::OpenGLES2 INTERFACE IMPORTED) 69 | set_property(TARGET OpenGLES2::OpenGLES2 APPEND PROPERTY 70 | INTERFACE_LINK_LIBRARIES ${OPENGLES2_LIBRARY}) 71 | else() 72 | add_library(OpenGLES2::OpenGLES2 UNKNOWN IMPORTED) 73 | set_property(TARGET OpenGLES2::OpenGLES2 PROPERTY 74 | IMPORTED_LOCATION ${OPENGLES2_LIBRARY}) 75 | endif() 76 | endif() 77 | 78 | mark_as_advanced(OPENGLES2_LIBRARY) 79 | -------------------------------------------------------------------------------- /primitives.conf: -------------------------------------------------------------------------------- 1 | [configuration] 2 | 3 | [configuration/capsule2DWireframe] 4 | hemisphereRings=8 5 | cylinderRings=1 6 | halfLength=0.75 7 | 8 | [configuration/capsule3DSolid] 9 | hemisphereRings=30 10 | cylinderRings=50 11 | segments=50 12 | halfLength=1.5 13 | textureCoordinates=false 14 | tangents=false 15 | 16 | [configuration/capsule3DWireframe] 17 | hemisphereRings=8 18 | cylinderRings=1 19 | segments=16 20 | halfLength=1.0 21 | 22 | [configuration/circle2DSolid] 23 | segments=16 24 | textureCoordinates=false 25 | 26 | [configuration/circle2DWireframe] 27 | segments=32 28 | 29 | [configuration/circle3DSolid] 30 | segments=16 31 | textureCoordinates=false 32 | tangents=false 33 | 34 | [configuration/circle3DWireframe] 35 | segments=32 36 | 37 | [configuration/coneSolid] 38 | rings=1 39 | segments=12 40 | halfLength=1.25 41 | textureCoordinates=false 42 | tangents=false 43 | capEnd=true 44 | 45 | [configuration/coneWireframe] 46 | segments=32 47 | halfLength=1.25 48 | 49 | [configuration/cylinderSolid] 50 | rings=1 51 | segments=12 52 | halfLength=1.0 53 | textureCoordinates=false 54 | tangents=false 55 | capEnds=true 56 | 57 | [configuration/cylinderWireframe] 58 | rings=1 59 | segments=32 60 | halfLength=1.0 61 | 62 | # Shared by gradient2D and gradient2DHorizontal / gradient2DVertical, in the 63 | # latter two cases the a / b is ignored 64 | [configuration/gradient2D] 65 | a=1.0 -2.0 66 | b=-1.0 2.0 67 | colorA=0.184314 0.513725 0.8 1.0 68 | colorB=0.231373 0.823529 0.403921 1.0 69 | 70 | # Shared by gradient3D and gradient3DHorizontal / gradient3DVertical, in the 71 | # latter two cases the a / b is ignored 72 | [configuration/gradient3D] 73 | a=1.0 -2.0 -1.5 74 | b=-1.0 2.0 -1.5 75 | colorA=0.184314 0.513725 0.8 1.0 76 | colorB=0.231373 0.823529 0.403921 1.0 77 | 78 | [configuration/grid3DSolid] 79 | subdivisions=5 3 80 | textureCoordinates=false 81 | tangents=false 82 | normals=true 83 | 84 | [configuration/grid3DWireframe] 85 | subdivisions=5 3 86 | 87 | [configuration/icosphereSolid] 88 | subdivisions=1 89 | 90 | [configuration/line2D] 91 | a=-1.0 0.0 92 | b=1.0 0.0 93 | 94 | [configuration/line3D] 95 | a=-1.0 0.0 0.0 96 | b=1.0 0.0 0.0 97 | 98 | [configuration/planeSolid] 99 | textureCoordinates=false 100 | tangents=false 101 | 102 | [configuration/squareSolid] 103 | textureCoordinates=false 104 | 105 | [configuration/uvSphereSolid] 106 | rings=8 107 | segments=16 108 | textureCoordinates=false 109 | tangents=false 110 | 111 | [configuration/uvSphereWireframe] 112 | rings=16 113 | segments=32 -------------------------------------------------------------------------------- /time-compile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | { time g++ "$@"; } 2> >(cat <(echo "g++ $@") - >> /tmp/results.txt) 3 | -------------------------------------------------------------------------------- /todos.txt: -------------------------------------------------------------------------------- 1 | 2 | high priority 3 | * finish remaining experiments 4 | - hill 5 | - sphere 6 | - hierarchical 7 | - connectedness for spot 8 | 9 | medium prio 10 | * use smoother step for W in connectedness constraint 11 | 12 | low prioritiy 13 | * fix webgl wireframe, also dont generate wireframe on each frame 14 | * fix touch input 15 | * fix projection matrix 16 | * nicer layout 17 | * host webpage on Phasefield repo. Move everything to github? 18 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------