PolyFEM
Loading...
Searching...
No Matches
CubicHermiteSplineParametrization.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <map>
4#include <vector>
5#include <set>
6#include <iostream>
7#include <Eigen/Dense>
8
9#include <polysolve/LinearSolver.hpp>
10
11namespace polyfem
12{
14 {
15 public:
16 CubicHermiteSplineParametrization(const std::map<int, Eigen::MatrixXd> &control_point, const std::map<int, Eigen::MatrixXd> &tangent, const std::map<int, std::vector<int>> &boundary_id_to_node_id, const Eigen::MatrixXd &V, const int sampling) : boundary_id_to_node_id_(boundary_id_to_node_id)
17 {
18 // Deduce the t parameter of all of the points in the spline sections
19 int index = 0;
20 double tol = 1e-4;
21 for (const auto &kv : boundary_id_to_node_id_)
22 {
23 std::vector<int> unused;
24 boundary_id_to_spline_count_[kv.first] = control_point.at(kv.first).rows() - 1;
25 for (const auto &b : kv.second)
26 {
27 Eigen::MatrixXd point = V.block(b, 0, 1, dim);
28 point.transposeInPlace();
29
30 int nearest;
31 double t_optimal, distance, distance_to_start, distance_to_end;
32 find_nearest_spline(point, control_point.at(kv.first), tangent.at(kv.first), nearest, t_optimal, distance, distance_to_start, distance_to_end);
33
34 if (nearest == -1 || distance > tol)
35 {
36 logger().error("Could not find a valid t for deducing spline parametrization. Distance: {}, spline: {}, point: {}, {}", distance, nearest, point(0), point(1));
37 unused.push_back(b);
38 continue;
39 }
40
41 node_id_to_t_[b] = t_optimal;
42 node_id_to_spline_[b] = nearest;
43 }
44
45 // Remove nodes that do not have a parametrization.
46 for (const auto &i : unused)
47 {
48 auto loc = std::find(boundary_id_to_node_id_[kv.first].begin(), boundary_id_to_node_id_[kv.first].end(), i);
49 if (loc == boundary_id_to_node_id_[kv.first].end())
50 logger().error("Error removing unused node.");
51 boundary_id_to_node_id_[kv.first].erase(loc);
52 }
53 logger().info("Number of useful boundary nodes in spline parametrization: {}", boundary_id_to_node_id_[kv.first].size());
54 }
55 }
56
57 void reparametrize(const std::map<int, Eigen::MatrixXd> &control_point, const std::map<int, Eigen::MatrixXd> &tangent, const Eigen::MatrixXd &V, Eigen::MatrixXd &newV) const
58 {
59 // Given new control parameters and the t parameter precomputed, compute new V
60 newV = V.block(0, 0, V.rows(), 2);
61 for (const auto &kv : boundary_id_to_node_id_)
62 {
63 for (const auto &b : kv.second)
64 {
65 Eigen::MatrixXd new_val;
66 eval(control_point.at(kv.first).block(node_id_to_spline_.at(b), 0, 2, dim), tangent.at(kv.first).block(2 * node_id_to_spline_.at(b), 0, 2, dim), node_id_to_t_.at(b), new_val);
67 newV.block(b, 0, 1, dim) = new_val;
68 }
69 }
70 }
71
72 // Assume the connectivity has not changed. Does not work with remeshing
73 void get_parameters(const Eigen::MatrixXd &V, std::map<int, Eigen::MatrixXd> &control_point, std::map<int, Eigen::MatrixXd> &tangent) const
74 {
75 // Deduce parameter values from vertex positions. This will involve fitting on an overdetermined system
76 for (const auto &kv : boundary_id_to_node_id_)
77 {
78 int spline_count = boundary_id_to_spline_count_.at(kv.first);
79 Eigen::MatrixXd A;
80 Eigen::MatrixXd b;
81 A.setZero(kv.second.size(), 3 * spline_count + 1);
82 b.setZero(kv.second.size(), 2);
83 int i = 0;
84 for (const int id : kv.second)
85 {
86 double t = node_id_to_t_.at(id);
87 double t_2 = pow(t, 2);
88 double t_3 = pow(t, 3);
89 int spline = node_id_to_spline_.at(id);
90 A(i, spline) = 2 * t_3 - 3 * t_2 + 1;
91 A(i, spline + 1) = -2 * t_3 + 3 * t_2;
92 A(i, spline_count + 1 + 2 * spline) = t_3 - 2 * t_2 + t;
93 A(i, spline_count + 1 + 2 * spline + 1) = t_3 - t_2;
94 b(i, 0) = V(id, 0);
95 b(i, 1) = V(id, 1);
96 i++;
97 }
98
99 Eigen::VectorXd x = (A.transpose() * A).ldlt().solve(A.transpose() * b.col(0));
100 Eigen::VectorXd y = (A.transpose() * A).ldlt().solve(A.transpose() * b.col(1));
101 double error_x = (A * x - b.col(0)).norm();
102 double error_y = (A * y - b.col(1)).norm();
103 Eigen::MatrixXd control_points(spline_count + 1, dim), tangents(2 * spline_count, dim);
104 control_points.col(0) = x.segment(0, spline_count + 1);
105 control_points.col(1) = y.segment(0, spline_count + 1);
106 tangents.col(0) = x.segment(spline_count + 1, 2 * spline_count);
107 tangents.col(1) = y.segment(spline_count + 1, 2 * spline_count);
108 control_point[kv.first] = control_points;
109 tangent[kv.first] = tangents;
110 }
111 }
112
113 void derivative_wrt_params(const Eigen::VectorXd &grad_boundary, const int boundary_id, const int couple_tangents, Eigen::VectorXd &grad_control_point, Eigen::VectorXd &grad_tangent) const
114 {
115 int spline_count = boundary_id_to_spline_count_.at(boundary_id);
116 grad_control_point.setZero(spline_count * dim + dim);
117 grad_tangent.setZero(spline_count * 2 * dim);
118 for (const auto &b : boundary_id_to_node_id_.at(boundary_id))
119 {
120 double t = node_id_to_t_.at(b);
121 double t_2 = pow(t, 2);
122 double t_3 = pow(t, 3);
123 int spline = node_id_to_spline_.at(b);
124 grad_control_point.segment(spline * dim, dim) += (2 * t_3 - 3 * t_2 + 1) * grad_boundary.segment(b * dim, dim);
125 grad_control_point.segment(spline * dim + dim, dim) += (-2 * t_3 + 3 * t_2) * grad_boundary.segment(b * dim, dim);
126 grad_tangent.segment(spline * 2 * dim, dim) += (t_3 - 2 * t_2 + t) * grad_boundary.segment(b * dim, dim);
127 grad_tangent.segment(spline * 2 * dim + dim, dim) += (t_3 - t_2) * grad_boundary.segment(b * dim, dim);
128 if (spline != 0)
129 {
130 int prev_spline = spline - 1;
131 if (couple_tangents)
132 grad_tangent.segment(prev_spline * 2 * dim + dim, dim) += (t_3 - 2 * t_2 + t) * grad_boundary.segment(b * dim, dim);
133 else
134 {
135 // The following is not correct, need to project onto normal of tangent.
136 grad_tangent.segment(prev_spline * 2 * dim + dim, dim) += (t_3 - 2 * t_2 + t) * grad_boundary.segment(b * dim, dim);
137 }
138 }
139 if (spline != spline_count - 1)
140 {
141 int next_spline = spline + 1;
142 if (couple_tangents)
143 grad_tangent.segment(next_spline * 2 * dim, dim) += (t_3 - t_2) * grad_boundary.segment(b * dim, dim);
144 else
145 {
146 // The following is not correct, need to project onto normal of tangent.
147 grad_tangent.segment(next_spline * 2 * dim, dim) += (t_3 - t_2) * grad_boundary.segment(b * dim, dim);
148 }
149 }
150 }
151 }
152
153 static void find_nearest_spline(const Eigen::MatrixXd &point, const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, int &nearest, double &t_optimal, double &distance, double &distance_to_start, double &distance_to_end, const double tol = 1e-4)
154 {
155 int dim = point.size();
156 int num_splines = control_point.rows() - 1;
157
158 auto dot = [](const Eigen::MatrixXd &a, const Eigen::MatrixXd &b) {
159 assert(a.cols() == 1);
160 assert(b.cols() == 1);
161 return (a.transpose() * b)(0);
162 };
163
164 auto f = [&](const double t, const int segment) {
165 Eigen::MatrixXd val;
166 eval(control_point.block(segment, 0, 2, dim), tangent.block(2 * segment, 0, 2, dim), t, val);
167 val.transposeInPlace();
168 return val;
169 };
170
171 auto f_ = [&](const double t, const int segment) {
172 Eigen::MatrixXd val;
173 deriv(control_point.block(segment, 0, 2, dim), tangent.block(2 * segment, 0, 2, dim), t, val);
174 val.transposeInPlace();
175 return val;
176 };
177
178 auto f__ = [&](const double t, const int segment) {
179 Eigen::MatrixXd val;
180 second_deriv(control_point.block(segment, 0, 2, dim), tangent.block(2 * segment, 0, 2, dim), t, val);
181 val.transposeInPlace();
182 return val;
183 };
184
185 auto g = [&](const double t, const int segment) {
186 auto val = f(t, segment);
187 return dot(val, val) - 2 * dot(point, val) + dot(point, point);
188 };
189
190 auto g_ = [&](const double t, const int segment) {
191 auto val = f(t, segment);
192 auto deriv = f_(t, segment);
193 return 2 * dot(deriv, val) - 2 * dot(point, deriv);
194 };
195
196 auto g__ = [&](const double t, const int segment) {
197 auto val = f(t, segment);
198 auto deriv = f_(t, segment);
199 auto sec_deriv = f__(t, segment);
200 return 2 * dot(sec_deriv, val - point) + 2 * dot(deriv, deriv);
201 };
202
203 Eigen::MatrixXd t = Eigen::MatrixXd::Ones(num_splines, 1) / 2.;
204
205 for (int i = 0; i < t.rows(); ++i)
206 {
207 for (int iter = 0; iter < 50; ++iter)
208 {
209 t(i) -= g_(t(i), i) / g__(t(i), i);
210 }
211 }
212
213 nearest = -1;
214 t_optimal = -1;
215 distance = -1.;
216 for (int i = 0; i < t.rows(); ++i)
217 {
218 if ((t(i) < -tol) || (t(i) > 1 + tol))
219 continue;
220 double func = g(t(i), i);
221 if ((nearest == -1) || (func < distance))
222 {
223 nearest = i;
224 t_optimal = t(i);
225 distance = func;
226 }
227 }
228
229 distance_to_start = g(0, 0);
230 distance_to_end = g(1, t.rows() - 1);
231 }
232
233 static void gradient(const Eigen::MatrixXd &point, const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, const int spline, const double t_parameter, const double distance, Eigen::MatrixXd &grad)
234 {
235 int dim = point.size();
236
237 auto f = [&](const double t, const int segment) {
238 Eigen::MatrixXd val;
239 eval(control_point.block(segment, 0, 2, dim), tangent.block(2 * segment, 0, 2, dim), t, val);
240 val.transposeInPlace();
241 return val;
242 };
243
244 grad.col(0).segment(0, dim) = (point - f(t_parameter, spline)) / distance;
245 }
246
247 static void eval(const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, const double t, Eigen::MatrixXd &val)
248 {
249 double t_2 = pow(t, 2);
250 double t_3 = pow(t, 3);
251 val = (2 * t_3 - 3 * t_2 + 1) * control_point.row(0);
252 val += (t_3 - 2 * t_2 + t) * tangent.row(0);
253 val += (-2 * t_3 + 3 * t_2) * control_point.row(1);
254 val += (t_3 - t_2) * tangent.row(1);
255 }
256
257 static void deriv(const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, const double t, Eigen::MatrixXd &val)
258 {
259 double t_2 = pow(t, 2);
260 val = (6 * t_2 - 6 * t) * control_point.row(0);
261 val += (3 * t_2 - 4 * t + 1) * tangent.row(0);
262 val += (-6 * t_2 + 6 * t) * control_point.row(1);
263 val += (3 * t_2 - 2 * t) * tangent.row(1);
264 }
265
266 static void second_deriv(const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, const double t, Eigen::MatrixXd &val)
267 {
268 val = (12 * t - 6) * control_point.row(0);
269 val += (6 * t - 4) * tangent.row(0);
270 val += (-12 * t + 6) * control_point.row(1);
271 val += (6 * t - 2) * tangent.row(1);
272 }
273
274 private:
275 std::map<int, std::vector<int>> boundary_id_to_node_id_;
276 std::map<int, double> node_id_to_t_;
277 std::map<int, int> node_id_to_spline_;
278 int dim = 2;
280 };
281} // namespace polyfem
int V
double val
Definition Assembler.cpp:86
int y
int x
void get_parameters(const Eigen::MatrixXd &V, std::map< int, Eigen::MatrixXd > &control_point, std::map< int, Eigen::MatrixXd > &tangent) const
static void eval(const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, const double t, Eigen::MatrixXd &val)
static void second_deriv(const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, const double t, Eigen::MatrixXd &val)
CubicHermiteSplineParametrization(const std::map< int, Eigen::MatrixXd > &control_point, const std::map< int, Eigen::MatrixXd > &tangent, const std::map< int, std::vector< int > > &boundary_id_to_node_id, const Eigen::MatrixXd &V, const int sampling)
static void gradient(const Eigen::MatrixXd &point, const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, const int spline, const double t_parameter, const double distance, Eigen::MatrixXd &grad)
void derivative_wrt_params(const Eigen::VectorXd &grad_boundary, const int boundary_id, const int couple_tangents, Eigen::VectorXd &grad_control_point, Eigen::VectorXd &grad_tangent) const
void reparametrize(const std::map< int, Eigen::MatrixXd > &control_point, const std::map< int, Eigen::MatrixXd > &tangent, const Eigen::MatrixXd &V, Eigen::MatrixXd &newV) const
static void deriv(const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, const double t, Eigen::MatrixXd &val)
static void find_nearest_spline(const Eigen::MatrixXd &point, const Eigen::MatrixXd &control_point, const Eigen::MatrixXd &tangent, int &nearest, double &t_optimal, double &distance, double &distance_to_start, double &distance_to_end, const double tol=1e-4)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42