8#ifndef IGL_WINDINGNUMBERANTIPODALSCENE_H
9#define IGL_WINDINGNUMBERANTIPODALSCENE_H
19#include <unordered_map>
48 template <
typename Scalar>
52 using Point = Eigen::Matrix<Scalar, 1, 3>;
69 template <
typename DerivedV,
typename DerivedF>
71 const Eigen::MatrixBase<DerivedV> & V,
72 const Eigen::MatrixBase<DerivedF> & F,
76 assert(V.cols() == 3 &&
"WindingNumberAntipodalScene: only 3D vertex positions are supported");
77 assert(F.cols() == 3 &&
"WindingNumberAntipodalScene: only triangle meshes are supported");
83 template <
typename Intersector,
typename Derivedp>
85 const Intersector & intersector,
86 const Eigen::MatrixBase<Derivedp> & p)
const
88 const Point pp(
static_cast<Scalar
>(p(0)),
89 static_cast<Scalar
>(p(1)),
90 static_cast<Scalar
>(p(2)));
92 Scalar area = Scalar(0);
95 const Point v0 = ws.a - pp;
96 const Point v1 = ws.b - pp;
99 const Scalar frac = area / (Scalar(2) * Scalar(
igl::PI));
101 using IO =
typename Intersector::OriginType;
102 using ID =
typename Intersector::DirectionType;
103 const IO io(
static_cast<typename IO::Scalar
>(pp(0)),
104 static_cast<typename IO::Scalar
>(pp(1)),
105 static_cast<typename IO::Scalar
>(pp(2)));
106 const ID id(
static_cast<typename ID::Scalar
>(
m_x0(0)),
107 static_cast<typename ID::Scalar
>(
m_x0(1)),
108 static_cast<typename ID::Scalar
>(
m_x0(2)));
109 const int c = intersector.signedIntersectionsRay(io,
id);
111 return frac + Scalar(c);
121 template <
typename Intersector,
typename DerivedO,
typename DerivedW>
123 const Intersector & intersector,
124 const Eigen::MatrixBase<DerivedO> & O,
125 Eigen::PlainObjectBase<DerivedW> & W)
const
127 W.resize(O.rows(), 1);
142 const double t_q_ns =
143 50.0 *
static_cast<double>(
m_boundary.size()) +
145 const size_t min_parallel =
static_cast<size_t>(
146 std::ceil(1.0e6 / std::max(t_q_ns, 1.0)));
150 W(o) = winding_number(intersector, O.row(o));
167 Scalar(std::sqrt(2.0)),
168 Scalar(std::sqrt(3.0)));
180 const Scalar l0 = v0.norm();
181 const Scalar l1 = v1.norm();
182 const Scalar num = x1.dot(v0.cross(v1));
183 const Scalar denom = l0 * l1
187 return std::atan2(num, denom);
199 template <
typename DerivedV,
typename DerivedF>
201 const Eigen::MatrixBase<DerivedV> & V,
202 const Eigen::MatrixBase<DerivedF> & F,
203 std::vector<WeightedSeg> & out)
206 std::unordered_map<std::uint64_t, int> counts;
207 counts.reserve(
static_cast<size_t>(F.rows()) * 3);
209 const auto pack = [](
int lo,
int hi) -> std::uint64_t
211 return (
static_cast<std::uint64_t
>(
static_cast<std::uint32_t
>(lo)) << 32)
212 |
static_cast<std::uint64_t
>(
static_cast<std::uint32_t
>(hi));
215 for (Eigen::Index t = 0; t < F.rows(); ++t)
218 static_cast<int>(F(t, 0)),
219 static_cast<int>(F(t, 1)),
220 static_cast<int>(F(t, 2))
222 for (
int e = 0; e < 3; ++e)
224 const int u = tri[e];
225 const int v = tri[(e + 1) % 3];
226 const int lo = u < v ? u : v;
227 const int hi = u < v ? v : u;
228 const std::uint64_t k = pack(lo, hi);
229 counts[k] += (u < v) ? +1 : -1;
233 out.reserve(counts.size());
234 for (
const auto & kv : counts)
236 const int sum = kv.second;
237 if (
sum == 0)
continue;
238 const std::uint32_t lo =
static_cast<std::uint32_t
>(kv.first >> 32);
239 const std::uint32_t hi =
static_cast<std::uint32_t
>(kv.first & 0xFFFFFFFFu);
240 const Point a(
static_cast<Scalar
>(V(lo, 0)),
241 static_cast<Scalar
>(V(lo, 1)),
242 static_cast<Scalar
>(V(lo, 2)));
243 const Point b(
static_cast<Scalar
>(V(hi, 0)),
244 static_cast<Scalar
>(V(hi, 1)),
245 static_cast<Scalar
>(V(hi, 2)));
247 if (
sum > 0) { ws.a = a; ws.b = b; ws.w =
static_cast<Scalar
>(
sum); }
248 else { ws.a = b; ws.b = a; ws.w =
static_cast<Scalar
>(-
sum); }
Eigen::Matrix< Scalar, 1, 3 > Point
Definition WindingNumberAntipodalScene.h:52
void winding_number(const Intersector &intersector, const Eigen::MatrixBase< DerivedO > &O, Eigen::PlainObjectBase< DerivedW > &W) const
Batch query, parallelized via igl::parallel_for.
Definition WindingNumberAntipodalScene.h:122
Direction m_x0
Definition WindingNumberAntipodalScene.h:254
static void build_boundary_segments(const Eigen::MatrixBase< DerivedV > &V, const Eigen::MatrixBase< DerivedF > &F, std::vector< WeightedSeg > &out)
Extract the open boundary as oriented, weighted edge segments.
Definition WindingNumberAntipodalScene.h:200
std::vector< WeightedSeg > m_boundary
Definition WindingNumberAntipodalScene.h:253
static Scalar half_solid_angle_unorm(const Direction &x1, const Point &v0, const Point &v1)
Half the signed solid angle subtended by the spherical triangle (x1, v0, v1) at the origin,...
Definition WindingNumberAntipodalScene.h:177
size_t num_faces() const
Number of triangles in the original mesh.
Definition WindingNumberAntipodalScene.h:159
const Direction & x0() const
Reference direction x0 used to evaluate this scene.
Definition WindingNumberAntipodalScene.h:155
Eigen::Matrix< Scalar, 1, 3 > Direction
Definition WindingNumberAntipodalScene.h:53
size_t m_face_count
Definition WindingNumberAntipodalScene.h:255
size_t num_boundary_segments() const
Number of weighted boundary edge segments.
Definition WindingNumberAntipodalScene.h:157
static Direction default_x0()
Default reference direction: normalize(1, sqrt(2), sqrt(3)).
Definition WindingNumberAntipodalScene.h:164
WindingNumberAntipodalScene(const Eigen::MatrixBase< DerivedV > &V, const Eigen::MatrixBase< DerivedF > &F, const Direction &x0=default_x0())
Build a scene from a 3D triangle mesh.
Definition WindingNumberAntipodalScene.h:70
Scalar winding_number(const Intersector &intersector, const Eigen::MatrixBase< Derivedp > &p) const
Single-point query: full generalized winding number at p (fractional + signed integer crossings).
Definition WindingNumberAntipodalScene.h:84
bool parallel_for(const Index loop_size, const FunctionType &func, const size_t min_parallel=0)
Functional implementation of a basic, open-mp style, parallel for loop.
Definition parallel_for.h:123
void sum(const Eigen::SparseMatrix< T > &X, const int dim, Eigen::SparseVector< T > &S)
Sum the columns or rows of a sparse matrix.
constexpr double PI
π
Definition PI.h:18