Blender V5.0
flip_avoiding_line_search.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2016 Michael Rabinovich
2 * 2023 Blender Authors
3 *
4 * SPDX-License-Identifier: MPL-2.0 */
5
9
11
12#include <Eigen/Dense>
13
14namespace slim {
15
16/* Implement a bisection linesearch to minimize a mesh-based energy on vertices given at 'x' at a
17 * search direction 'd', with initial step size. Stops when a point with lower energy is found, or
18 * after maximal iterations have been reached.
19 *
20 * Inputs:
21 * x #X by dim list of variables
22 * d #X by dim list of a given search direction
23 * step_size initial step size
24 * energy A function to compute the mesh-based energy (return an energy that is
25 * bigger than 0) cur_energy(OPTIONAL) The energy at the given point. Helps save redundant
26 * computations.
27 * This is optional. If not specified, the function will compute it.
28 * Outputs:
29 * x #X by dim list of variables at the new location
30 * Returns the energy at the new point 'x'.
31 */
32static inline double line_search(Eigen::MatrixXd &x,
33 const Eigen::MatrixXd &d,
34 double step_size,
35 std::function<double(Eigen::MatrixXd &)> energy,
36 double cur_energy = -1)
37{
38 double old_energy;
39 if (cur_energy > 0) {
40 old_energy = cur_energy;
41 }
42 else {
43 old_energy = energy(x); /* No energy was given -> need to compute the current energy. */
44 }
45 double new_energy = old_energy;
46 int cur_iter = 0;
47 int MAX_STEP_SIZE_ITER = 12;
48
49 while (new_energy >= old_energy && cur_iter < MAX_STEP_SIZE_ITER) {
50 Eigen::MatrixXd new_x = x + step_size * d;
51
52 double cur_e = energy(new_x);
53 if (cur_e >= old_energy) {
54 step_size /= 2;
55 }
56 else {
57 x = new_x;
58 new_energy = cur_e;
59 }
60 cur_iter++;
61 }
62 return new_energy;
63}
64
65static inline double get_smallest_pos_quad_zero(double a, double b, double c)
66{
67 using namespace std;
68 double t1, t2;
69 if (a != 0) {
70 double delta_in = pow(b, 2) - 4 * a * c;
71 if (delta_in < 0) {
72 return INFINITY;
73 }
74 double delta = sqrt(delta_in);
75 t1 = (-b + delta) / (2 * a);
76 t2 = (-b - delta) / (2 * a);
77 }
78 else {
79 t1 = t2 = -b / c;
80 }
81
82 if (!std::isfinite(t1) || !std::isfinite(t2)) {
83 throw SlimFailedException();
84 }
85
86 double tmp_n = min(t1, t2);
87 t1 = max(t1, t2);
88 t2 = tmp_n;
89 if (t1 == t2) {
90 return INFINITY; /* Means the orientation flips twice = doesn't flip. */
91 }
92 /* Return the smallest negative root if it exists, otherwise return infinity. */
93 if (t1 > 0) {
94 if (t2 > 0) {
95 return t2;
96 }
97 return t1;
98 }
99 return INFINITY;
100}
101
102static inline double get_min_pos_root_2D(const Eigen::MatrixXd &uv,
103 const Eigen::MatrixXi &F,
104 Eigen::MatrixXd &d,
105 int f)
106{
107 using namespace std;
108 /* Finding the smallest timestep t s.t a triangle get degenerated (<=> det = 0). */
109 int v1 = F(f, 0);
110 int v2 = F(f, 1);
111 int v3 = F(f, 2);
112 /* Get quadratic coefficients (ax^2 + b^x + c). */
113 const double &U11 = uv(v1, 0);
114 const double &U12 = uv(v1, 1);
115 const double &U21 = uv(v2, 0);
116 const double &U22 = uv(v2, 1);
117 const double &U31 = uv(v3, 0);
118 const double &U32 = uv(v3, 1);
119
120 const double &V11 = d(v1, 0);
121 const double &V12 = d(v1, 1);
122 const double &V21 = d(v2, 0);
123 const double &V22 = d(v2, 1);
124 const double &V31 = d(v3, 0);
125 const double &V32 = d(v3, 1);
126
127 double a = V11 * V22 - V12 * V21 - V11 * V32 + V12 * V31 + V21 * V32 - V22 * V31;
128 double b = U11 * V22 - U12 * V21 - U21 * V12 + U22 * V11 - U11 * V32 + U12 * V31 + U31 * V12 -
129 U32 * V11 + U21 * V32 - U22 * V31 - U31 * V22 + U32 * V21;
130 double c = U11 * U22 - U12 * U21 - U11 * U32 + U12 * U31 + U21 * U32 - U22 * U31;
131
132 return get_smallest_pos_quad_zero(a, b, c);
133}
134
135static inline double compute_max_step_from_singularities(const Eigen::MatrixXd &uv,
136 const Eigen::MatrixXi &F,
137 Eigen::MatrixXd &d)
138{
139 using namespace std;
140 double max_step = INFINITY;
141
142 /* The if statement is outside the for loops to avoid branching/ease parallelizing. */
143 for (int f = 0; f < F.rows(); f++) {
144 double min_positive_root = get_min_pos_root_2D(uv, F, d, f);
145 max_step = min(max_step, min_positive_root);
146 }
147 return max_step;
148}
149
150inline double flip_avoiding_line_search(const Eigen::MatrixXi F,
151 Eigen::MatrixXd &cur_v,
152 Eigen::MatrixXd &dst_v,
153 std::function<double(Eigen::MatrixXd &)> energy,
154 double cur_energy)
155{
156 using namespace std;
157
158 Eigen::MatrixXd d = dst_v - cur_v;
159 double min_step_to_singularity = compute_max_step_from_singularities(cur_v, F, d);
160 double max_step_size = min(1., min_step_to_singularity * 0.8);
161 return line_search(cur_v, d, max_step_size, energy, cur_energy);
162}
163
164} // namespace slim
ATTR_WARN_UNUSED_RESULT const BMVert * v2
#define pow
#define sqrt
#define F
static double get_min_pos_root_2D(const Eigen::MatrixXd &uv, const Eigen::MatrixXi &F, Eigen::MatrixXd &d, int f)
static double get_smallest_pos_quad_zero(double a, double b, double c)
static double line_search(Eigen::MatrixXd &x, const Eigen::MatrixXd &d, double step_size, std::function< double(Eigen::MatrixXd &)> energy, double cur_energy=-1)
static double compute_max_step_from_singularities(const Eigen::MatrixXd &uv, const Eigen::MatrixXi &F, Eigen::MatrixXd &d)
double flip_avoiding_line_search(const Eigen::MatrixXi F, Eigen::MatrixXd &cur_v, Eigen::MatrixXd &dst_v, std::function< double(Eigen::MatrixXd &)> energy, double cur_energy)
#define min(a, b)
Definition sort.cc:36
max
Definition text_draw.cc:251