6#include <igl/boundary_conditions.h>
8#include <igl/boundary_loop.h>
9#include <igl/exact_geodesic.h>
10#include <igl/bounding_box.h>
11#include <igl/writeOBJ.h>
15#include <unsupported/Eigen/SparseExtra>
17#include <unordered_map>
24 Eigen::Matrix<double, 3, 1> affine_transformation(
const Eigen::Matrix<double, 3, 1> &control_pt,
const Eigen::Matrix<double, 3, 1> &point,
const Eigen::Matrix<double, 6, 1> ¶m)
26 Eigen::Matrix<double, 3, 1> transformed_point(3);
28 const double helper_0 = cos(param(2));
29 const double helper_1 = control_pt(0) - point(0);
30 const double helper_2 = cos(param(1));
31 const double helper_3 = helper_1 * helper_2;
32 const double helper_4 = control_pt(2) - point(2);
33 const double helper_5 = sin(param(0));
34 const double helper_6 = sin(param(2));
35 const double helper_7 = helper_5 * helper_6;
36 const double helper_8 = sin(param(1));
37 const double helper_9 = cos(param(0));
38 const double helper_10 = helper_0 * helper_9;
39 const double helper_11 = control_pt(1) - point(1);
40 const double helper_12 = helper_6 * helper_9;
41 const double helper_13 = helper_0 * helper_5;
42 transformed_point(0) = control_pt(0) - helper_0 * helper_3 - helper_11 * (-helper_12 + helper_13 * helper_8) - helper_4 * (helper_10 * helper_8 + helper_7) + param(3);
43 transformed_point(1) = control_pt(1) - helper_11 * (helper_10 + helper_7 * helper_8) - helper_3 * helper_6 + helper_4 * (-helper_12 * helper_8 + helper_13) + param(4);
44 transformed_point(2) = control_pt(2) + helper_1 * helper_8 - helper_11 * helper_2 * helper_5 - helper_2 * helper_4 * helper_9 + param(5);
46 return transformed_point;
49 Eigen::MatrixXd grad_affine_transformation(
const Eigen::Matrix<double, 3, 1> &control_pt,
const Eigen::VectorXd &point,
const Eigen::VectorXd ¶m)
51 Eigen::MatrixXd
grad(6, 3);
53 const double helper_0 = control_pt(1) - point(1);
54 const double helper_1 = sin(param(0));
55 const double helper_2 = sin(param(2));
56 const double helper_3 = helper_1 * helper_2;
57 const double helper_4 = sin(param(1));
58 const double helper_5 = cos(param(0));
59 const double helper_6 = cos(param(2));
60 const double helper_7 = helper_5 * helper_6;
61 const double helper_8 = helper_3 + helper_4 * helper_7;
62 const double helper_9 = control_pt(2) - point(2);
63 const double helper_10 = helper_2 * helper_5;
64 const double helper_11 = helper_1 * helper_6;
65 const double helper_12 = -helper_10 + helper_11 * helper_4;
66 const double helper_13 = control_pt(0) - point(0);
67 const double helper_14 = cos(param(1));
68 const double helper_15 = helper_0 * helper_1;
69 const double helper_16 = helper_5 * helper_9;
70 const double helper_17 = helper_13 * helper_4 - helper_14 * helper_15 - helper_14 * helper_16;
71 const double helper_18 = helper_13 * helper_14;
72 const double helper_19 = helper_3 * helper_4 + helper_7;
73 const double helper_20 = -helper_10 * helper_4 + helper_11;
74 grad(0) = -helper_0 * helper_8 + helper_12 * helper_9;
75 grad(1) = helper_17 * helper_6;
76 grad(2) = helper_0 * helper_19 + helper_18 * helper_2 - helper_20 * helper_9;
80 grad(6) = helper_0 * helper_20 + helper_19 * helper_9;
81 grad(7) = helper_17 * helper_2;
82 grad(8) = -helper_0 * helper_12 - helper_18 * helper_6 - helper_8 * helper_9;
86 grad(12) = helper_14 * (-helper_0 * helper_5 + helper_1 * helper_9);
87 grad(13) = helper_15 * helper_4 + helper_16 * helper_4 + helper_18;
93 return grad.transpose();
100 return exclude_ends_ ? 2 * (control_num - 2) : 2 * control_num;
118 Eigen::MatrixXd new_control_points;
122 for (
int i = 1; i < new_control_points.rows() - 1; ++i)
123 new_control_points.row(i) =
x.segment(2 * i - 2, 2);
129 Eigen::MatrixXd new_vertices;
130 spline_->reparametrize(new_control_points, new_vertices);
137 Eigen::VectorXd grad;
138 spline_->derivative_wrt_params(grad_full, grad);
146 : num_control_vertices_(num_control_vertices), num_vertices_(num_vertices), allow_rotations_(allow_rotations)
154 const auto &mesh = state.
mesh;
155 const auto &bases = state.
bases;
159 const int e = lb.element_id();
160 for (
int i = 0; i < lb.size(); ++i)
162 const int primitive_global_id = lb.global_primitive_id(i);
163 const int boundary_id = mesh->get_boundary_id(primitive_global_id);
164 const auto nodes = gbases[e].local_nodes_for_primitive(primitive_global_id, *mesh);
166 for (
int f = 0; f < nodes.size(); ++f)
168 F_surface_(f_size - 1, f) = map[gbases[e].bases[nodes(f)].global()[0].index];
173 for (
int e = 0; e < gbases.size(); e++)
175 for (
const auto &gbs : gbases[e].bases)
176 V_surface_.row(map[gbs.global()[0].index]) = gbs.global()[0].node;
182 std::set<int> fixed_vertices;
184 for (
int i = 0; i < boundary_loop.size(); ++i)
185 fixed_vertices.insert(boundary_loop(i));
186 for (
const auto &i : existing_points)
187 fixed_vertices.insert(i);
190 Eigen::VectorXi free_vertices(
V.rows() - fixed_vertices.size());
192 for (
int i = 0; i <
V.rows(); ++i)
193 if (fixed_vertices.find(i) == fixed_vertices.end())
194 free_vertices(s++) = i;
196 Eigen::VectorXi VS, FS, FT;
197 VS.resize(fixed_vertices.size());
199 for (
const auto &j : fixed_vertices)
203 igl::exact_geodesic(
V,
F, VS, FS, free_vertices, FT, d);
205 double max_min_dist = -1;
206 for (
int i = 0; i < d.size(); ++i)
211 opt_idx = free_vertices(i);
214 else if (d(i) > max_min_dist)
217 opt_idx = free_vertices(i);
237 Eigen::VectorXi outer_loop;
238 std::vector<std::vector<int>> loops;
239 igl::boundary_loop(
F, loops);
240 if (loops.size() == 0)
242 logger().info(
"Found 0 boundary loops, must be closed surface.");
244 else if (loops.size() == 1)
246 logger().info(
"Found 1 boundary loop, must be disk topology.");
247 outer_loop.resize(loops[0].
size());
248 for (
int i = 0; i < loops[0].size(); ++i)
249 outer_loop(i) = loops[0][i];
253 logger().error(
"More than 1 boundary loop! Concatenating and continuing as normal.");
254 for (
const auto &l : loops)
256 int size = outer_loop.size();
257 outer_loop.conservativeResize(outer_loop.size() + l.size());
258 for (
int i = 0; i < l.size(); ++i)
259 outer_loop(
size + i) = l[i];
262 Eigen::MatrixXd V_outer_loop =
V(outer_loop, Eigen::all);
266 std::vector<int> control_indices;
268 std::set<int> possible_control_vertices;
269 for (
int i = 0; i <
F.rows(); ++i)
270 for (
int j = 0; j <
F.cols(); ++j)
271 possible_control_vertices.insert(
F(i, j));
272 for (
int i = 0; i < outer_loop.size(); ++i)
273 possible_control_vertices.erase(outer_loop(i));
277 const int recompute_loops = 5;
278 for (
int r = 0; r < recompute_loops; ++r)
282 std::vector<int> indices = control_indices;
283 indices.erase(indices.begin() + i);
285 control_indices[i] = new_idx;
294 igl::writeOBJ(
"bbw_control_points_" + std::to_string(
V.rows()) +
".obj",
control_points_, Eigen::MatrixXi::Zero(0, 3));
295 igl::writeOBJ(
"bbw_outer_loop_" + std::to_string(
V.rows()) +
".obj", V_outer_loop, Eigen::MatrixXi::Zero(0, 3));
299 Eigen::VectorXi point_handles_idx(point_handles.rows());
300 for (
int i = 0; i < point_handles_idx.size(); ++i)
301 point_handles_idx(i) = i;
302 igl::boundary_conditions(
V,
F, point_handles, point_handles_idx, Eigen::VectorXi(), Eigen::VectorXi(), Eigen::VectorXi(), b, bc);
304 igl::BBWData bbw_data;
305 bbw_data.active_set_params.max_iter = 500;
306 bbw_data.verbosity = 1;
307 bbw_data.partition_unity =
false;
308 Eigen::MatrixXd complete_bbw_weights;
309 bool computation = igl::bbw(
V,
F, b, bc, bbw_data, complete_bbw_weights);
313 complete_bbw_weights = (complete_bbw_weights.array().colwise() / complete_bbw_weights.array().rowwise().sum()).
eval();
317 igl::writeOBJ(
"surface_mesh.obj",
V,
F);
318 Eigen::saveMarket(
bbw_weights_.sparseView(0, 1e-8).eval(),
"bbw_control_weights.mat");
330 Eigen::VectorXd
y = Eigen::VectorXd::Zero(
y_start.size());
335 Eigen::Matrix<double, 6, 1> affine_params =
x.segment(j * 6, 6);
356 Eigen::VectorXd grad = Eigen::VectorXd::Zero(
x.size());
361 grad.segment(j * 6, 6) +=
bbw_weights_(i, j) * grad_affine_transformation(
control_points_.row(j),
y_start.segment(i * 3, 3),
x.segment(j * 6, 6)).transpose() * grad_full.segment(i * 3, 3);
367 grad.segment(j * 3, 3) +=
bbw_weights_(i, j) * grad_full.segment(i * 3, 3);
375 std::unordered_map<int, int> full_to_reduced_indices;
379 for (
int i = 0; i <
V.rows(); ++i)
383 BV.col(0) =
V.row(i);
384 BV.col(1) =
V.row(i);
388 for (
int j = 0; j < 3; ++j)
390 BV(j, 0) = std::min(BV(j, 0),
V(i, j));
391 BV(j, 1) = std::max(BV(j, 1),
V(i, j));
396 Eigen::VectorXd bbox_width = (BV.col(1) - BV.col(0));
397 for (
int j = 0; j < 3; ++j)
398 if (bbox_width(j) < 1e-12)
399 bbox_width(j) = 1e-3;
402 BV.col(0) -= 0.05 * (BV.col(1) - BV.col(0));
403 BV.col(1) += 0.05 * (BV.col(1) - BV.col(0));
405 auto in_bbox = [&BV](
const Eigen::VectorXd &
x) {
407 in &= (
x(0) >= BV(0, 0)) && (
x(0) <= BV(0, 1));
408 in &= (
x(1) >= BV(1, 0)) && (
x(1) <= BV(1, 1));
409 in &= (
x(2) >= BV(2, 0)) && (
x(0) <= BV(2, 1));
414 for (
int j = 0; j <
V.rows(); ++j)
418 full_to_reduced_indices[i] = j;
424 bool contains_face =
true;
425 for (
int j = 0; j < 3; ++j)
426 contains_face &= (full_to_reduced_indices.count(
F_surface_(i, j)) == 1);
430 F.conservativeResize(
F.rows() + 1, 3);
431 for (
int j = 0; j < 3; ++j)
432 F(
F.rows() - 1, j) = full_to_reduced_indices.at(
F_surface_(i, j));
main class that contains the polyfem solver and all its state
const std::vector< basis::ElementBases > & geom_bases() const
Get a constant reference to the geometry mapping bases.
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
std::vector< basis::ElementBases > bases
FE bases, the size is #elements.
void get_vertices(Eigen::MatrixXd &vertices) const
Gather geometry vertices into a dense matrix.
std::vector< mesh::LocalBoundary > total_local_boundary
mapping from elements to nodes for all mesh
std::vector< int > node_to_primitive() const
bool invoked_inverse_eval_
int inverse_size(int y_size) const override
Compute DOF of x given DOF of y.
const Eigen::VectorXd knots_
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad_full, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
std::shared_ptr< BSplineParametrization2D > spline_
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
const Eigen::MatrixXd initial_control_points_
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) const override
Eval x = f^-1 (y).
Eigen::MatrixXi F_surface_
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
Eigen::MatrixXd boundary_bbw_weights_
const int num_control_vertices_
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad_full, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
Eigen::MatrixXd control_points_
Eigen::MatrixXd V_surface_
void compute_faces_for_partial_vertices(const Eigen::MatrixXd &V, Eigen::MatrixXi &F) const
const bool allow_rotations_
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) const override
Eval x = f^-1 (y).
Eigen::MatrixXd bbw_weights_
bool invoked_inverse_eval_
int size(const int x_size) const override
Compute DOF of y given DOF of x.
BoundedBiharmonicWeights2Dto3D(const int num_control_vertices, const int num_vertices, const Eigen::MatrixXd &V_surface, const Eigen::MatrixXi &F_surface)
int inverse_size(int y_size) const override
Compute DOF of x given DOF of y.
int optimal_new_control_point_idx(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::VectorXi &boundary_loop, const std::vector< int > &existing_points) const
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
Eigen::VectorXd flatten(const Eigen::MatrixXd &X)
Flatten rowwises.
spdlog::logger & logger()
Retrieves the current logger.
void log_and_throw_error(const std::string &msg)