Wildmeshing Toolkit
Loading...
Searching...
No Matches
WindingNumberHierarchy.hpp
1// Adapted from libigl's WindingNumberTree and WindingNumberAABB.
2//
3// Copyright (C) 2014 Alec Jacobson <alecjacobson@gmail.com>
4//
5// This Source Code Form is subject to the terms of the Mozilla Public License
6// v. 2.0. If a copy of the MPL was not distributed with this file, You can
7// obtain one at http://mozilla.org/MPL/2.0/.
8#pragma once
9
10#include <Eigen/Core>
11
12// clang-format off
13#include <wmtk/utils/DisableWarnings.hpp>
14#include <igl/barycenter.h>
15#include <igl/exterior_edges.h>
16#include <igl/median.h>
17#include <igl/remove_duplicate_vertices.h>
18#include <igl/winding_number.h>
19#include <wmtk/utils/EnableWarnings.hpp>
20// clang-format on
21
22#include <limits>
23#include <vector>
24
25namespace wmtk::utils {
26
50{
51public:
52 WindingNumberHierarchy(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F)
53 {
54 Eigen::MatrixXi SVI, SVJ, SF;
55 igl::remove_duplicate_vertices(V, F, 0.0, m_V, SVI, SVJ, SF);
56 add_node(std::move(SF));
57 grow(0);
58 }
59
61 double winding_number(const Eigen::RowVector3d& p) const { return node_winding_number(0, p); }
62
63private:
65 static constexpr Eigen::Index kMinFaces = 100;
66
67 struct Node
68 {
69 Eigen::MatrixXi F; // faces, into m_V
70 Eigen::MatrixXi cap; // the fan closing F's boundary
71 Eigen::RowVector3d min_corner, max_corner; // the box of F's vertices
72 int left = -1, right = -1;
73 };
74
76 static Eigen::MatrixXi cap_of(const Eigen::MatrixXi& F)
77 {
78 const Eigen::MatrixXi E = igl::exterior_edges(F);
79 if (E.size() == 0) return Eigen::MatrixXi(0, 3);
80 const int s = E(0, 0);
81 std::vector<Eigen::Index> rows;
82 for (Eigen::Index i = 0; i < E.rows(); ++i) {
83 if (E(i, 0) != s && E(i, 1) != s) rows.push_back(i);
84 }
85 Eigen::MatrixXi cap(rows.size(), 3);
86 for (size_t k = 0; k < rows.size(); ++k) {
87 cap.row(k) << s, E(rows[k], 0), E(rows[k], 1);
88 }
89 return cap;
90 }
91
92 int add_node(Eigen::MatrixXi F)
93 {
94 Node n;
95 n.cap = cap_of(F);
96 n.min_corner.setConstant(std::numeric_limits<double>::infinity());
97 n.max_corner.setConstant(-std::numeric_limits<double>::infinity());
98 for (Eigen::Index i = 0; i < F.rows(); ++i) {
99 for (Eigen::Index j = 0; j < F.cols(); ++j) {
100 n.min_corner = n.min_corner.cwiseMin(m_V.row(F(i, j)));
101 n.max_corner = n.max_corner.cwiseMax(m_V.row(F(i, j)));
102 }
103 }
104 n.F = std::move(F);
105 m_nodes.push_back(std::move(n));
106 return int(m_nodes.size()) - 1;
107 }
108
110 void grow(const int node)
111 {
112 // Indices, not references: add_node below may reallocate m_nodes.
113 const Eigen::Index n_faces = m_nodes[node].F.rows();
114 if (n_faces <= kMinFaces || m_nodes[node].cap.rows() - 2 >= n_faces) return;
115
116 int axis = -1;
117 double longest = -std::numeric_limits<double>::infinity();
118 for (int d = 0; d < 3; ++d) {
119 const double len = m_nodes[node].max_corner[d] - m_nodes[node].min_corner[d];
120 if (len > longest) {
121 longest = len;
122 axis = d;
123 }
124 }
125 Eigen::MatrixXd BC;
126 igl::barycenter(m_V, m_nodes[node].F, BC);
127 double split_value;
128 igl::median(BC.col(axis), split_value);
129
130 std::vector<Eigen::Index> left, right;
131 for (Eigen::Index i = 0; i < n_faces; ++i) {
132 (BC(i, axis) <= split_value ? left : right).push_back(i);
133 }
134 if (left.empty() || right.empty()) return;
135 const auto rows_of = [&](const std::vector<Eigen::Index>& rows) {
136 Eigen::MatrixXi F(rows.size(), 3);
137 for (size_t k = 0; k < rows.size(); ++k) F.row(k) = m_nodes[node].F.row(rows[k]);
138 return F;
139 };
140 const int l = add_node(rows_of(left));
141 m_nodes[node].left = l;
142 grow(l);
143 const int r = add_node(rows_of(right));
144 m_nodes[node].right = r;
145 grow(r);
146 }
147
148 bool inside(const Node& n, const Eigen::RowVector3d& p) const
149 {
150 // Conservative, as igl's: a point on the box is inside.
151 for (int d = 0; d < 3; ++d) {
152 if (p[d] < n.min_corner[d] || p[d] > n.max_corner[d]) return false;
153 }
154 return true;
155 }
156
158 double node_winding_number(const int node, const Eigen::RowVector3d& p) const
159 {
160 const Node& n = m_nodes[node];
161 if (inside(n, p)) {
162 if (n.left >= 0) {
163 double sum = 0;
164 sum += node_winding_number(n.left, p);
165 sum += node_winding_number(n.right, p);
166 return sum;
167 }
168 return igl::winding_number(m_V, n.F, p);
169 }
170 // Outside the box the boundary decides, when it is the cheaper of the two.
171 if (n.cap.rows() - 2 < n.F.rows()) return igl::winding_number(m_V, n.cap, p);
172 return igl::winding_number(m_V, n.F, p);
173 }
174
175 Eigen::MatrixXd m_V; // the mesh's vertices, exact duplicates merged
176 std::vector<Node> m_nodes; // m_nodes[0] is the root
177};
178
179} // namespace wmtk::utils
The hierarchy libigl's winding_number(V, F, O, W) evaluates a triangle mesh with – igl::WindingNumber...
Definition WindingNumberHierarchy.hpp:50
static Eigen::MatrixXi cap_of(const Eigen::MatrixXi &F)
igl::triangle_fan(igl::exterior_edges(F)), with a fixed apex instead of a random one.
Definition WindingNumberHierarchy.hpp:76
double node_winding_number(const int node, const Eigen::RowVector3d &p) const
WindingNumberTree::winding_number, exact method.
Definition WindingNumberHierarchy.hpp:158
static constexpr Eigen::Index kMinFaces
Fewest faces a node is split at, as igl's WindingNumberAABB_MIN_F.
Definition WindingNumberHierarchy.hpp:65
double winding_number(const Eigen::RowVector3d &p) const
Winding number of p with respect to the mesh.
Definition WindingNumberHierarchy.hpp:61
void grow(const int node)
WindingNumberAABB::grow with the median split.
Definition WindingNumberHierarchy.hpp:110
Definition WindingNumberHierarchy.hpp:68