Wildmeshing Toolkit
orient.cpp
Go to the documentation of this file.
1 #include "orient.hpp"
2 #include "predicates.h"
3 
4 // clang-format off
5 #include <VolumeRemesher/numerics.h>
6 // clang-format on
7 #include <iomanip>
8 #include <iostream>
9 #include <limits>
10 #include <numbers>
11 
12 namespace wmtk::utils {
13 
14 vol_rem::interval_number rational_to_interval(const Rational& r)
15 {
16  if (r.is_rounded())
17  return vol_rem::interval_number(r.to_double());
18  else {
19  const double inf = std::numeric_limits<double>::max();
20  const double d = r.to_double();
21 
22  if (r < 0) return vol_rem::interval_number(-std::nextafter(d, -inf), d);
23  if (r > 0) return vol_rem::interval_number(-d, std::nextafter(d, inf));
24  return vol_rem::interval_number(0);
25  }
26 }
27 
28 void exactinit()
29 {
30  // Thread-safe initialization using Meyers' singleton
31  class MySingleton
32  {
33  public:
34  static MySingleton& instance()
35  {
36  static MySingleton instance;
37  return instance;
38  }
39 
40  private:
41  MySingleton() { ::exactinit(); }
42  };
43  MySingleton::instance();
44 }
45 
46 namespace {
47 bool is_rounded(const Eigen::Ref<const Eigen::Vector3<Rational>>& p)
48 {
49  return p[0].is_rounded() && p[1].is_rounded() && p[2].is_rounded();
50 }
51 bool is_rounded(const Eigen::Ref<const Eigen::Vector2<Rational>>& p)
52 {
53  return p[0].is_rounded() && p[1].is_rounded();
54 }
55 
56 template <typename T>
57 T determinant(const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, 0, 3, 3>& mat)
58 {
59  assert(mat.rows() == mat.cols());
60 
61  if (mat.rows() == 1)
62  return mat(0);
63  else if (mat.rows() == 2)
64  return mat(0, 0) * mat(1, 1) - mat(0, 1) * mat(1, 0);
65  else if (mat.rows() == 3)
66  return mat(0, 0) * (mat(1, 1) * mat(2, 2) - mat(1, 2) * mat(2, 1)) -
67  mat(0, 1) * (mat(1, 0) * mat(2, 2) - mat(1, 2) * mat(2, 0)) +
68  mat(0, 2) * (mat(1, 0) * mat(2, 1) - mat(1, 1) * mat(2, 0));
69 
70  assert(false);
71  return T();
72 }
73 
74 } // namespace
76  const Eigen::Ref<const Eigen::Vector3<Rational>>& p0,
77  const Eigen::Ref<const Eigen::Vector3<Rational>>& p1,
78  const Eigen::Ref<const Eigen::Vector3<Rational>>& p2,
79  const Eigen::Ref<const Eigen::Vector3<Rational>>& p3)
80 {
81  static bool initialized = false;
82  if (!initialized) {
83  vol_rem::initFPU();
84  initialized = true;
85  }
86 
87  if (is_rounded(p0) && is_rounded(p1) && is_rounded(p2) && is_rounded(p3)) {
88  return wmtk_orient3d(
89  p0.cast<double>(),
90  p1.cast<double>(),
91  p2.cast<double>(),
92  p3.cast<double>());
93  } else {
94  // Fast version using intervals
95  Eigen::Vector3<vol_rem::interval_number> p0r_i;
96  Eigen::Vector3<vol_rem::interval_number> p1r_i;
97  Eigen::Vector3<vol_rem::interval_number> p2r_i;
98  Eigen::Vector3<vol_rem::interval_number> p3r_i;
99 
100  vol_rem::setFPUModeToRoundUP();
101  for (int64_t i = 0; i < 3; ++i) {
102  p0r_i[i] = rational_to_interval(p0[i]);
103  p1r_i[i] = rational_to_interval(p1[i]);
104  p2r_i[i] = rational_to_interval(p2[i]);
105  p3r_i[i] = rational_to_interval(p3[i]);
106  }
107  Eigen::Matrix3<vol_rem::interval_number> M_i;
108  M_i.row(0) = p0r_i - p3r_i;
109  M_i.row(1) = p1r_i - p3r_i;
110  M_i.row(2) = p2r_i - p3r_i;
111 
112 
113  const auto det_i = determinant<vol_rem::interval_number>(M_i);
114  auto reliable = det_i.signIsReliable();
115  auto sign = det_i.sign();
116  vol_rem::setFPUModeToRoundNEAR();
117  // assert(!det.is_rounded());
118  if (reliable) {
119  return sign;
120  }
121 
122  // Slow version using rationals
123  Eigen::Vector3<Rational> p0r;
124  Eigen::Vector3<Rational> p1r;
125  Eigen::Vector3<Rational> p2r;
126  Eigen::Vector3<Rational> p3r;
127 
128  for (int64_t i = 0; i < 3; ++i) {
129  p0r[i] = Rational(p0[i], false);
130  p1r[i] = Rational(p1[i], false);
131  p2r[i] = Rational(p2[i], false);
132  p3r[i] = Rational(p3[i], false);
133  }
134 
135  Eigen::Matrix3<Rational> M;
136  M.row(0) = p0r - p3r;
137  M.row(1) = p1r - p3r;
138  M.row(2) = p2r - p3r;
139 
140 #ifndef NDEBUG
141  for (int i = 0; i < 3; ++i) {
142  for (int j = 0; j < 3; ++j) {
143  assert(!M(i, j).is_rounded());
144  }
145  }
146 #endif
147 
148  const auto det = determinant<Rational>(M);
149  assert(!det.is_rounded());
150 
151  return det.get_sign();
152  }
153 }
154 
156  const Eigen::Ref<const Eigen::Vector3<double>>& p0,
157  const Eigen::Ref<const Eigen::Vector3<double>>& p1,
158  const Eigen::Ref<const Eigen::Vector3<double>>& p2,
159  const Eigen::Ref<const Eigen::Vector3<double>>& p3)
160 {
161  Eigen::Vector3d p0nc = p0;
162  Eigen::Vector3d p1nc = p1;
163  Eigen::Vector3d p2nc = p2;
164  Eigen::Vector3d p3nc = p3;
165 
166  exactinit();
167  const auto res = orient3d(p0nc.data(), p1nc.data(), p2nc.data(), p3nc.data());
168 
169  if (res > 0)
170  return 1;
171  else if (res < 0)
172  return -1;
173  else
174  return 0;
175 }
176 
177 
178 int wmtk_orient2d(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y)
179 {
180  exactinit();
181  double p0[2]{p0x, p0y};
182  double p1[2]{p1x, p1y};
183  double p2[2]{p2x, p2y};
184  const auto res = orient2d(p0, p1, p2);
185 
186  if (res > 0)
187  return 1;
188  else if (res < 0)
189  return -1;
190  else
191  return 0;
192 }
193 
195  const Eigen::Ref<const Eigen::Vector2<Rational>>& p0,
196  const Eigen::Ref<const Eigen::Vector2<Rational>>& p1,
197  const Eigen::Ref<const Eigen::Vector2<Rational>>& p2)
198 {
199  if (is_rounded(p0) && is_rounded(p1) && is_rounded(p2)) {
200  return wmtk_orient2d(p0.cast<double>(), p1.cast<double>(), p2.cast<double>());
201  } else {
202 
203  // Fast version using intervals
204  Eigen::Vector2<vol_rem::interval_number> p0r_i;
205  Eigen::Vector2<vol_rem::interval_number> p1r_i;
206  Eigen::Vector2<vol_rem::interval_number> p2r_i;
207 
208  vol_rem::setFPUModeToRoundUP();
209  for (int64_t i = 0; i < 2; ++i) {
210  p0r_i[i] = rational_to_interval(p0[i]);
211  p1r_i[i] = rational_to_interval(p1[i]);
212  p2r_i[i] = rational_to_interval(p2[i]);
213  }
214 
215  Eigen::Matrix2<vol_rem::interval_number> M_i;
216  M_i.row(0) = p1r_i - p0r_i;
217  M_i.row(1) = p2r_i - p0r_i;
218 
219  const auto det_i = determinant<vol_rem::interval_number>(M_i);
220  auto reliable = det_i.signIsReliable();
221  auto sign = det_i.sign();
222  vol_rem::setFPUModeToRoundNEAR();
223 
224  if (reliable) {
225  return sign;
226  }
227 
228  // Slow version using rationals
229  Eigen::Vector2<Rational> p0r;
230  Eigen::Vector2<Rational> p1r;
231  Eigen::Vector2<Rational> p2r;
232 
233  for (int64_t i = 0; i < 2; ++i) {
234  p0r[i] = Rational(p0[i], false);
235  p1r[i] = Rational(p1[i], false);
236  p2r[i] = Rational(p2[i], false);
237  }
238 
239  Eigen::Matrix2<Rational> M;
240  M.row(0) = p1r - p0r;
241  M.row(1) = p2r - p0r;
242  const auto det = determinant<Rational>(M);
243  return det.get_sign();
244  }
245 }
246 
248  const Eigen::Ref<const Eigen::Vector2<double>>& p0,
249  const Eigen::Ref<const Eigen::Vector2<double>>& p1,
250  const Eigen::Ref<const Eigen::Vector2<double>>& p2)
251 {
252  Eigen::Vector2d p0nc = p0;
253  Eigen::Vector2d p1nc = p1;
254  Eigen::Vector2d p2nc = p2;
255 
256  exactinit();
257  const auto res = orient2d(p0nc.data(), p1nc.data(), p2nc.data());
258 
259  if (res > 0)
260  return 1;
261  else if (res < 0)
262  return -1;
263  else
264  return 0;
265 }
266 
267 int wmtk_orient1d(const Rational& p0, const Rational& p1)
268 {
269  return p0 == p1 ? 0 : (p0 < p1 ? -1 : 1);
270 }
271 
272 int wmtk_orient1d(double p0, double p1)
273 {
274  return p0 == p1 ? 0 : (p0 < p1 ? -1 : 1);
275 }
276 
277 } // namespace wmtk::utils
double to_double() const
Definition: Rational.cpp:317
bool is_rounded() const
Definition: Rational.hpp:86
int get_sign() const
Definition: Rational.cpp:15
wmtk::Rational det(const wmtk::Vector2r &a, const wmtk::Vector2r &b)
int wmtk_orient3d(const Eigen::Ref< const Eigen::Vector3< Rational >> &p0, const Eigen::Ref< const Eigen::Vector3< Rational >> &p1, const Eigen::Ref< const Eigen::Vector3< Rational >> &p2, const Eigen::Ref< const Eigen::Vector3< Rational >> &p3)
Definition: orient.cpp:75
void exactinit()
Definition: orient.cpp:28
int wmtk_orient1d(const Rational &p0, const Rational &p1)
Definition: orient.cpp:267
vol_rem::interval_number rational_to_interval(const Rational &r)
Definition: orient.cpp:14
int wmtk_orient2d(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y)
Definition: orient.cpp:178
Vector< double, 3 > Vector3d
Definition: Types.hpp:39