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