Wildmeshing Toolkit
Loading...
Searching...
No Matches
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
12namespace wmtk::utils {
13
14vol_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
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
46namespace {
47bool 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}
51bool is_rounded(const Eigen::Ref<const Eigen::Vector2<Rational>>& p)
52{
53 return p[0].is_rounded() && p[1].is_rounded();
54}
55
56template <typename T>
57T 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
178int 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
267int wmtk_orient1d(const Rational& p0, const Rational& p1)
268{
269 return p0 == p1 ? 0 : (p0 < p1 ? -1 : 1);
270}
271
272int 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
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_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
int wmtk_orient2d(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y)
Definition orient.cpp:178