Skip to content

Commit ff0ad6c

Browse files
committed
Fixed discrete metric methods
1 parent 81cf528 commit ff0ad6c

19 files changed

+451
-189
lines changed

.DS_Store

-2 KB
Binary file not shown.

CMakeLists.txt

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/util")
7979
include(conformal_ideal_delaunay)
8080
include(libigl)
8181
include(spectra)
82+
include(cli11)
8283

8384
# Optionally create visualization library
8485
if(RENDER_TEXTURE)
@@ -108,15 +109,6 @@ endif()
108109
# Make main cpp library
109110
add_subdirectory(src)
110111

111-
112-
if(USE_SUITESPARSE)
113-
# include(suitesparse)
114-
# target_link_libraries(PennerOptimizationLib PRIVATE
115-
# SuiteSparse::SuiteSparseConfig
116-
# SuiteSparse::CHOLMOD
117-
# )
118-
endif()
119-
120112
# Build pybind optimization functions
121113
if(USE_PYBIND)
122114
include(pybind11)
@@ -161,14 +153,6 @@ if(NOT USE_MULTIPRECISION)
161153
if(BUILD_CURVATURE_METRIC_TESTS)
162154
include(Catch2)
163155

164-
# Build testing libraries
165-
#add_library(CurvatureMetricTestsLib
166-
#)
167-
#target_link_libraries(CurvatureMetricTestsLib PUBLIC
168-
# PennerOptimizationLib
169-
# Catch2::Catch2WithMain
170-
#)
171-
172156
# Build testing executable
173157
add_executable(CurvatureMetricTests
174158
src/tests/tests.cpp

cmake/cli11.cmake

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
if(TARGET CLI11::CLI11)
2+
return()
3+
endif()
4+
5+
include(FetchContent)
6+
FetchContent_Declare(
7+
cli11
8+
SYSTEM
9+
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
10+
GIT_TAG v2.3.2
11+
)
12+
FetchContent_MakeAvailable(cli11)

scripts/script_util.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,16 @@ def generate_mesh(args, fname=None):
146146

147147
# Build energies (default to 2-norm)
148148
energy_choice = args['energy_choice']
149-
if (energy_choice == "p_norm"):
149+
if (energy_choice == "log_length"):
150150
opt_energy = opt.LogLengthEnergy(C_embed, args['power'])
151-
elif (energy_choice == "scale_distortion"):
151+
elif (energy_choice == "log_scale"):
152152
opt_energy = opt.LogScaleEnergy(C_embed)
153153
elif (energy_choice == "quadratic_sym_dirichlet"):
154154
opt_energy = opt.QuadraticSymmetricDirichletEnergy(C_embed, C_eucl)
155155
elif (energy_choice == "sym_dirichlet"):
156156
opt_energy = opt.SymmetricDirichletEnergy(C_embed, C_eucl)
157+
elif (energy_choice == "p_norm"):
158+
opt_energy = opt.LogLengthEnergy(C_embed, args['power'])
157159
else:
158160
opt_energy = opt.LogLengthEnergy(C_embed, 2)
159161

src/app/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ add_executable(optimize_metric
1515
optimize_metric.cpp
1616
)
1717
target_link_libraries(optimize_metric PRIVATE
18-
PennerOptimizationLib
18+
PennerOptimizationLib
19+
CLI11::CLI11
1920
)
2021

2122
add_executable(optimize_shear

src/app/optimize_metric.cpp

Lines changed: 150 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,163 @@
1+
#include <igl/readOBJ.h>
2+
#include <igl/writeOBJ.h>
13
#include "common.hh"
2-
#include "implicit_optimization.hh"
34
#include "cone_metric.hh"
45
#include "energy_functor.hh"
6+
#include "implicit_optimization.hh"
7+
#include "io.hh"
58
#include "penner_optimization_interface.hh"
69
#include "refinement.hh"
7-
#include "io.hh"
810
#include "vector.hh"
9-
#include <igl/readOBJ.h>
10-
#include <igl/writeOBJ.h>
11+
#include "viewers.hh"
12+
13+
#include <CLI/CLI.hpp>
14+
1115
using namespace CurvatureMetric;
1216

13-
int main(int argc, char *argv[])
17+
int main(int argc, char* argv[])
1418
{
1519
#ifdef MULTIPRECISION
16-
spdlog::info("Using multiprecision");
17-
mpfr::mpreal::set_default_prec(100);
18-
mpfr::mpreal::set_emax(mpfr::mpreal::get_emax_max());
19-
mpfr::mpreal::set_emin(mpfr::mpreal::get_emin_min());
20+
spdlog::info("Using multiprecision");
21+
mpfr::mpreal::set_default_prec(100);
22+
mpfr::mpreal::set_emax(mpfr::mpreal::get_emax_max());
23+
mpfr::mpreal::set_emin(mpfr::mpreal::get_emin_min());
2024
#endif
2125

22-
spdlog::set_level(spdlog::level::info);
23-
assert(argc > 3);
24-
std::string input_filename = argv[1];
25-
std::string Th_hat_filename = argv[2];
26-
std::string output_dir = argv[3];
27-
std::filesystem::create_directories(output_dir);
28-
29-
// Get input mesh
30-
Eigen::MatrixXd V, uv, N;
31-
Eigen::MatrixXi F, FT, FN;
32-
spdlog::info("Optimizing mesh at {}", input_filename);
33-
igl::readOBJ(input_filename, V, uv, N, F, FT, FN);
34-
35-
// Get input angles
36-
std::vector<Scalar> Th_hat_init;
37-
spdlog::info("Using cone angles at {}", Th_hat_filename);
38-
read_vector_from_file(Th_hat_filename, Th_hat_init);
39-
std::vector<Scalar> Th_hat = correct_cone_angles(Th_hat_init);
40-
41-
// Get initial mesh for optimization
42-
std::vector<int> vtx_reindex;
43-
std::vector<int> free_cones = {};
44-
bool fix_boundary = false;
45-
std::unique_ptr<DifferentiableConeMetric> cone_metric = generate_initial_mesh(V, F, V, F, Th_hat, vtx_reindex, free_cones, fix_boundary, false);
46-
std::unique_ptr<DifferentiableConeMetric> eucl_cone_metric = generate_initial_mesh(V, F, V, F, Th_hat, vtx_reindex, free_cones, fix_boundary, true);
47-
DiscreteMetric discrete_metric(*eucl_cone_metric, eucl_cone_metric->get_metric_coordinates());
48-
49-
// Get energy
50-
QuadraticSymmetricDirichletEnergy opt_energy(*cone_metric, discrete_metric);
51-
52-
// Get initial metric from file or target
53-
VectorX reduced_metric_init;
54-
if (argc > 4)
55-
{
56-
std::string metric_filename = argv[4];
57-
std::vector<Scalar> reduced_metric_init_vec;
58-
read_vector_from_file(metric_filename, reduced_metric_init_vec);
59-
convert_std_to_eigen_vector(reduced_metric_init_vec, reduced_metric_init);
60-
cone_metric->set_metric_coordinates(reduced_metric_init);
61-
}
62-
63-
// Set all cones as dof
64-
//std::vector<int> cone_vertices;
65-
//compute_cone_vertices(m, cone_vertices);
66-
//std::vector<bool> is_cone_vertex;
67-
//convert_index_vector_to_boolean_array(cone_vertices, m.n_vertices(), is_cone_vertex);
68-
//m.fixed_dof = is_cone_vertex;
69-
70-
// Make default parameters with specified output directory
71-
auto proj_params = std::make_shared<ProjectionParameters>();
72-
auto opt_params = std::make_shared<OptimizationParameters>();
73-
opt_params->output_dir = output_dir;
74-
75-
// Optimize the metric
76-
std::unique_ptr<DifferentiableConeMetric> optimized_cone_metric = optimize_metric(
77-
*cone_metric,
78-
opt_energy,
79-
proj_params,
80-
opt_params);
81-
VectorX optimized_metric_coords = optimized_cone_metric->get_metric_coordinates();
82-
83-
// Write the output
84-
std::string output_filename = join_path(output_dir, "optimized_metric_coords");
85-
write_vector(optimized_metric_coords, output_filename, 17);
86-
87-
// Generate overlay VF mesh with parametrization
88-
std::vector<bool> is_cut = {};
89-
bool do_best_fit_scaling = false;
90-
auto vf_res = generate_VF_mesh_from_metric(
91-
V,
92-
F,
93-
Th_hat,
94-
*cone_metric,
95-
optimized_metric_coords,
96-
is_cut,
97-
do_best_fit_scaling
98-
);
99-
OverlayMesh<Scalar> m_o = std::get<0>(vf_res);
100-
Eigen::MatrixXd V_o = std::get<1>(vf_res);
101-
Eigen::MatrixXi F_o = std::get<2>(vf_res);
102-
Eigen::MatrixXd uv_o = std::get<3>(vf_res);
103-
Eigen::MatrixXi FT_o = std::get<4>(vf_res);
104-
std::vector<int> fn_to_f_o = std::get<7>(vf_res);
105-
std::vector<std::pair<int,int>> endpoints_o = std::get<8>(vf_res);
106-
107-
// Write the overlay output
108-
output_filename = join_path(output_dir, "overlay_mesh_with_uv.obj");
109-
write_obj_with_uv(output_filename, V_o, F_o, uv_o, FT_o);
110-
111-
// Get refinement mesh
112-
// Build mesh
113-
Eigen::MatrixXd V_r;
114-
Eigen::MatrixXi F_r;
115-
Eigen::MatrixXd uv_r;
116-
Eigen::MatrixXi FT_r;
117-
std::vector<int> fn_to_f_r;
118-
std::vector<std::pair<int,int>> endpoints_r;
119-
RefinementMesh refinement_mesh(V_o, F_o, uv_o, FT_o, fn_to_f_o, endpoints_o);
120-
refinement_mesh.get_VF_mesh(V_r, F_r, uv_r, FT_r, fn_to_f_r, endpoints_r);
121-
122-
// Write the refined output
123-
output_filename = join_path(output_dir, "refined_mesh_with_uv.obj");
124-
write_obj_with_uv(output_filename, V_r, F_r, uv_r, FT_r);
125-
}
26+
// Build maps from strings to enums
27+
std::map<std::string, EnergyChoice> energy_choice_map{
28+
{"log_length", EnergyChoice::log_length},
29+
{"log_scale", EnergyChoice::log_scale},
30+
{"quadratic_sym_dirichlet", EnergyChoice::quadratic_sym_dirichlet},
31+
{"sym_dirichlet", EnergyChoice::sym_dirichlet},
32+
{"p_norm", EnergyChoice::p_norm},
33+
};
34+
35+
// Get command line arguments
36+
CLI::App app{"Generate approximately isometric parameterization for a mesh."};
37+
std::string mesh_filename = "";
38+
std::string Th_hat_filename = "";
39+
std::string output_dir = "./";
40+
EnergyChoice energy_choice = EnergyChoice::log_length;
41+
bool use_discrete_metric = false;
42+
bool show_parameterization = false;
43+
auto proj_params = std::make_shared<ProjectionParameters>();
44+
auto opt_params = std::make_shared<OptimizationParameters>();
45+
app.add_option("--mesh", mesh_filename, "Mesh filepath")->check(CLI::ExistingFile)->required();
46+
app.add_option("--cones", Th_hat_filename, "Cone angle filepath")
47+
->check(CLI::ExistingFile)
48+
->required();
49+
app.add_option("--energy", energy_choice, "Energy to minimize")
50+
->transform(CLI::CheckedTransformer(energy_choice_map, CLI::ignore_case));
51+
app.add_option("--direction", opt_params->direction_choice, "Descent direction: projected_gradient, projected_newton");
52+
app.add_option(
53+
"--num_iter",
54+
opt_params->num_iter,
55+
"Maximum number of iterations to perform")
56+
->check(CLI::NonNegativeNumber);
57+
app.add_flag("--use_discrete_metric", use_discrete_metric, "Use edge lengths instead of Penner coordinates");
58+
app.add_flag("--show_parameterization", show_parameterization, "Show final parameterization");
59+
app.add_option("-o,--output", output_dir, "Output directory");
60+
CLI11_PARSE(app, argc, argv);
61+
62+
spdlog::set_level(spdlog::level::info);
63+
std::filesystem::create_directories(output_dir);
64+
opt_params->output_dir = output_dir;
65+
66+
// TODO Make this automatic
67+
if (use_discrete_metric)
68+
{
69+
proj_params->initial_ptolemy = false;
70+
proj_params->use_edge_flips = false;
71+
proj_params->max_itr = 30;
72+
}
12673

74+
// Get input mesh
75+
Eigen::MatrixXd V, uv, N;
76+
Eigen::MatrixXi F, FT, FN;
77+
spdlog::info("Optimizing mesh at {}", mesh_filename);
78+
igl::readOBJ(mesh_filename, V, uv, N, F, FT, FN);
79+
80+
// Get input angles
81+
std::vector<Scalar> Th_hat_init;
82+
spdlog::info("Using cone angles at {}", Th_hat_filename);
83+
read_vector_from_file(Th_hat_filename, Th_hat_init);
84+
std::vector<Scalar> Th_hat = correct_cone_angles(Th_hat_init);
85+
86+
// Get initial mesh for optimization
87+
std::vector<int> vtx_reindex;
88+
std::vector<int> free_cones = {};
89+
bool fix_boundary = false;
90+
std::unique_ptr<DifferentiableConeMetric> cone_metric =
91+
generate_initial_mesh(V, F, V, F, Th_hat, vtx_reindex, free_cones, fix_boundary, use_discrete_metric);
92+
93+
// Get energy
94+
std::unique_ptr<EnergyFunctor> opt_energy = generate_energy(V, F, Th_hat, *cone_metric, energy_choice);
95+
96+
// Optimize the metric
97+
std::unique_ptr<DifferentiableConeMetric> optimized_cone_metric =
98+
optimize_metric(*cone_metric, *opt_energy, proj_params, opt_params);
99+
VectorX optimized_metric_coords = optimized_cone_metric->get_reduced_metric_coordinates();
100+
101+
// Write the output metric coordinates
102+
std::string output_filename = join_path(output_dir, "optimized_metric_coords");
103+
write_vector(optimized_metric_coords, output_filename, 17);
104+
105+
// Generate overlay VF mesh with parametrization
106+
if (use_discrete_metric) {
107+
auto vf_res = generate_VF_mesh_from_discrete_metric(
108+
V,
109+
F,
110+
Th_hat,
111+
optimized_metric_coords);
112+
Eigen::MatrixXd V_l = std::get<0>(vf_res);
113+
Eigen::MatrixXi F_l = std::get<1>(vf_res);
114+
Eigen::MatrixXd uv_l = std::get<2>(vf_res);
115+
Eigen::MatrixXi FT_l = std::get<3>(vf_res);
116+
117+
// Write the overlay output
118+
output_filename = join_path(output_dir, "mesh_with_uv.obj");
119+
write_obj_with_uv(output_filename, V_l, F_l, uv_l, FT_l);
120+
121+
// Optionally show final parameterization
122+
if (show_parameterization) view_parameterization(V_l, F_l, uv_l, FT_l);
123+
} else {
124+
std::vector<bool> is_cut = {};
125+
bool do_best_fit_scaling = false;
126+
auto vf_res = generate_VF_mesh_from_metric(
127+
V,
128+
F,
129+
Th_hat,
130+
*cone_metric,
131+
optimized_metric_coords,
132+
is_cut,
133+
do_best_fit_scaling);
134+
OverlayMesh<Scalar> m_o = std::get<0>(vf_res);
135+
Eigen::MatrixXd V_o = std::get<1>(vf_res);
136+
Eigen::MatrixXi F_o = std::get<2>(vf_res);
137+
Eigen::MatrixXd uv_o = std::get<3>(vf_res);
138+
Eigen::MatrixXi FT_o = std::get<4>(vf_res);
139+
std::vector<int> fn_to_f_o = std::get<7>(vf_res);
140+
std::vector<std::pair<int, int>> endpoints_o = std::get<8>(vf_res);
141+
142+
// Write the overlay output
143+
output_filename = join_path(output_dir, "overlay_mesh_with_uv.obj");
144+
write_obj_with_uv(output_filename, V_o, F_o, uv_o, FT_o);
145+
146+
// Get refinement mesh
147+
Eigen::MatrixXd V_r;
148+
Eigen::MatrixXi F_r;
149+
Eigen::MatrixXd uv_r;
150+
Eigen::MatrixXi FT_r;
151+
std::vector<int> fn_to_f_r;
152+
std::vector<std::pair<int, int>> endpoints_r;
153+
RefinementMesh refinement_mesh(V_o, F_o, uv_o, FT_o, fn_to_f_o, endpoints_o);
154+
refinement_mesh.get_VF_mesh(V_r, F_r, uv_r, FT_r, fn_to_f_r, endpoints_r);
155+
156+
// Write the refined output
157+
output_filename = join_path(output_dir, "refined_mesh_with_uv.obj");
158+
write_obj_with_uv(output_filename, V_r, F_r, uv_r, FT_r);
159+
160+
// Optionally show final parameterization
161+
if (show_parameterization) view_parameterization(V_r, F_r, uv_r, FT_r);
162+
}
163+
}

src/core/cone_metric.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,11 @@ void PennerConeMetric::reset()
238238
m_flip_seq.clear();
239239
}
240240

241+
MatrixX PennerConeMetric::get_expansion_matrix() const
242+
{
243+
return m_identification * m_projection;
244+
}
245+
241246
VectorX PennerConeMetric::reduce_metric_coordinates(const VectorX& metric_coords) const
242247
{
243248
int num_reduced_coordinates = m_embed.size();
@@ -342,6 +347,11 @@ MatrixX DiscreteMetric::get_transition_jacobian() const
342347
return m_identification * m_projection;
343348
}
344349

350+
MatrixX DiscreteMetric::get_expansion_matrix() const
351+
{
352+
return m_identification * m_projection;
353+
}
354+
345355
VectorX DiscreteMetric::reduce_metric_coordinates(const VectorX& metric_coords) const
346356
{
347357
int num_reduced_coordinates = m_embed.size();

0 commit comments

Comments
 (0)