Fixing dep build script on Windows and removing some warnings. Use bundled igl by default. Not building with the dependency scripts if not explicitly stated. This way, it will stay in Fix the libigl patch to include C source files in header only mode.
325 lines
15 KiB
C++
325 lines
15 KiB
C++
// This file is part of libigl, a simple c++ geometry processing library.
|
|
//
|
|
// Copyright (C) 2015 Qingnan Zhou <qnzhou@gmail.com>
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public License
|
|
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
// obtain one at http://mozilla.org/MPL/2.0/.
|
|
//
|
|
#include "propagate_winding_numbers.h"
|
|
#include "../../extract_manifold_patches.h"
|
|
#include "../../extract_non_manifold_edge_curves.h"
|
|
#include "../../facet_components.h"
|
|
#include "../../unique_edge_map.h"
|
|
#include "../../piecewise_constant_winding_number.h"
|
|
#include "../../writeOBJ.h"
|
|
#include "../../writePLY.h"
|
|
#include "../../get_seconds.h"
|
|
#include "../../LinSpaced.h"
|
|
#include "order_facets_around_edge.h"
|
|
#include "outer_facet.h"
|
|
#include "closest_facet.h"
|
|
#include "assign.h"
|
|
#include "extract_cells.h"
|
|
#include "cell_adjacency.h"
|
|
|
|
#include <stdexcept>
|
|
#include <limits>
|
|
#include <vector>
|
|
#include <tuple>
|
|
#include <queue>
|
|
|
|
//#define PROPAGATE_WINDING_NUMBER_TIMING
|
|
|
|
template<
|
|
typename DerivedV,
|
|
typename DerivedF,
|
|
typename DerivedL,
|
|
typename DerivedW>
|
|
IGL_INLINE bool igl::copyleft::cgal::propagate_winding_numbers(
|
|
const Eigen::PlainObjectBase<DerivedV>& V,
|
|
const Eigen::PlainObjectBase<DerivedF>& F,
|
|
const Eigen::PlainObjectBase<DerivedL>& labels,
|
|
Eigen::PlainObjectBase<DerivedW>& W)
|
|
{
|
|
#ifdef PROPAGATE_WINDING_NUMBER_TIMING
|
|
const auto & tictoc = []() -> double
|
|
{
|
|
static double t_start = igl::get_seconds();
|
|
double diff = igl::get_seconds()-t_start;
|
|
t_start += diff;
|
|
return diff;
|
|
};
|
|
const auto log_time = [&](const std::string& label) -> void {
|
|
std::cout << "propagate_winding_num." << label << ": "
|
|
<< tictoc() << std::endl;
|
|
};
|
|
tictoc();
|
|
#endif
|
|
|
|
Eigen::MatrixXi E, uE;
|
|
Eigen::VectorXi EMAP;
|
|
std::vector<std::vector<size_t> > uE2E;
|
|
igl::unique_edge_map(F, E, uE, EMAP, uE2E);
|
|
|
|
Eigen::VectorXi P;
|
|
const size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P);
|
|
|
|
DerivedW per_patch_cells;
|
|
const size_t num_cells =
|
|
igl::copyleft::cgal::extract_cells(
|
|
V, F, P, E, uE, uE2E, EMAP, per_patch_cells);
|
|
#ifdef PROPAGATE_WINDING_NUMBER_TIMING
|
|
log_time("cell_extraction");
|
|
#endif
|
|
|
|
return propagate_winding_numbers(V, F,
|
|
uE, uE2E,
|
|
num_patches, P,
|
|
num_cells, per_patch_cells,
|
|
labels, W);
|
|
}
|
|
|
|
|
|
template<
|
|
typename DerivedV,
|
|
typename DerivedF,
|
|
typename DeriveduE,
|
|
typename uE2EType,
|
|
typename DerivedP,
|
|
typename DerivedC,
|
|
typename DerivedL,
|
|
typename DerivedW>
|
|
IGL_INLINE bool igl::copyleft::cgal::propagate_winding_numbers(
|
|
const Eigen::PlainObjectBase<DerivedV>& V,
|
|
const Eigen::PlainObjectBase<DerivedF>& F,
|
|
const Eigen::PlainObjectBase<DeriveduE>& uE,
|
|
const std::vector<std::vector<uE2EType> >& uE2E,
|
|
const size_t num_patches,
|
|
const Eigen::PlainObjectBase<DerivedP>& P,
|
|
const size_t num_cells,
|
|
const Eigen::PlainObjectBase<DerivedC>& C,
|
|
const Eigen::PlainObjectBase<DerivedL>& labels,
|
|
Eigen::PlainObjectBase<DerivedW>& W)
|
|
{
|
|
#ifdef PROPAGATE_WINDING_NUMBER_TIMING
|
|
const auto & tictoc = []() -> double
|
|
{
|
|
static double t_start = igl::get_seconds();
|
|
double diff = igl::get_seconds()-t_start;
|
|
t_start += diff;
|
|
return diff;
|
|
};
|
|
const auto log_time = [&](const std::string& label) -> void {
|
|
std::cout << "propagate_winding_num." << label << ": "
|
|
<< tictoc() << std::endl;
|
|
};
|
|
tictoc();
|
|
#endif
|
|
|
|
bool valid = true;
|
|
// https://github.com/libigl/libigl/issues/674
|
|
if (!igl::piecewise_constant_winding_number(F, uE, uE2E))
|
|
{
|
|
assert(false && "Input mesh is not PWN");
|
|
std::cerr << "Input mesh is not PWN!" << std::endl;
|
|
valid = false;
|
|
}
|
|
|
|
const size_t num_faces = F.rows();
|
|
typedef std::tuple<typename DerivedC::Scalar, bool, size_t> CellConnection;
|
|
std::vector<std::set<CellConnection> > cell_adj;
|
|
igl::copyleft::cgal::cell_adjacency(C, num_cells, cell_adj);
|
|
#ifdef PROPAGATE_WINDING_NUMBER_TIMING
|
|
log_time("cell_connectivity");
|
|
#endif
|
|
|
|
auto save_cell = [&](const std::string& filename, size_t cell_id) -> void{
|
|
std::vector<size_t> faces;
|
|
for (size_t i=0; i<num_patches; i++) {
|
|
if ((C.row(i).array() == cell_id).any()) {
|
|
for (size_t j=0; j<num_faces; j++) {
|
|
if ((size_t)P[j] == i) {
|
|
faces.push_back(j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Eigen::MatrixXi cell_faces(faces.size(), 3);
|
|
for (size_t i=0; i<faces.size(); i++) {
|
|
cell_faces.row(i) = F.row(faces[i]);
|
|
}
|
|
Eigen::MatrixXd vertices;
|
|
assign(V,vertices);
|
|
writePLY(filename, vertices, cell_faces);
|
|
};
|
|
|
|
#ifndef NDEBUG
|
|
{
|
|
// Check for odd cycle.
|
|
Eigen::VectorXi cell_labels(num_cells);
|
|
cell_labels.setZero();
|
|
Eigen::VectorXi parents(num_cells);
|
|
parents.setConstant(-1);
|
|
auto trace_parents = [&](size_t idx) -> std::list<size_t> {
|
|
std::list<size_t> path;
|
|
path.push_back(idx);
|
|
while ((size_t)parents[path.back()] != path.back()) {
|
|
path.push_back(parents[path.back()]);
|
|
}
|
|
return path;
|
|
};
|
|
for (size_t i=0; i<num_cells; i++) {
|
|
if (cell_labels[i] == 0) {
|
|
cell_labels[i] = 1;
|
|
std::queue<size_t> Q;
|
|
Q.push(i);
|
|
parents[i] = i;
|
|
while (!Q.empty()) {
|
|
size_t curr_idx = Q.front();
|
|
Q.pop();
|
|
int curr_label = cell_labels[curr_idx];
|
|
for (const auto& neighbor : cell_adj[curr_idx]) {
|
|
if (cell_labels[std::get<0>(neighbor)] == 0)
|
|
{
|
|
cell_labels[std::get<0>(neighbor)] = curr_label * -1;
|
|
Q.push(std::get<0>(neighbor));
|
|
parents[std::get<0>(neighbor)] = curr_idx;
|
|
} else
|
|
{
|
|
if (cell_labels[std::get<0>(neighbor)] != curr_label * -1)
|
|
{
|
|
std::cerr << "Odd cell cycle detected!" << std::endl;
|
|
auto path = trace_parents(curr_idx);
|
|
path.reverse();
|
|
auto path2 = trace_parents(std::get<0>(neighbor));
|
|
path.insert(path.end(), path2.begin(), path2.end());
|
|
for (auto cell_id : path)
|
|
{
|
|
std::cout << cell_id << " ";
|
|
std::stringstream filename;
|
|
filename << "cell_" << cell_id << ".ply";
|
|
save_cell(filename.str(), cell_id);
|
|
}
|
|
std::cout << std::endl;
|
|
valid = false;
|
|
}
|
|
// Do not fail when odd cycle is detected because the resulting
|
|
// integer winding number field, although inconsistent, may still
|
|
// be used if the problem region is local and embedded within a
|
|
// valid volume.
|
|
//assert(cell_labels[std::get<0>(neighbor)] == curr_label * -1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef PROPAGATE_WINDING_NUMBER_TIMING
|
|
log_time("odd_cycle_check");
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
size_t outer_facet;
|
|
bool flipped;
|
|
Eigen::VectorXi I = igl::LinSpaced<Eigen::VectorXi>(num_faces, 0, num_faces-1);
|
|
igl::copyleft::cgal::outer_facet(V, F, I, outer_facet, flipped);
|
|
#ifdef PROPAGATE_WINDING_NUMBER_TIMING
|
|
log_time("outer_facet");
|
|
#endif
|
|
|
|
const size_t outer_patch = P[outer_facet];
|
|
const size_t infinity_cell = C(outer_patch, flipped?1:0);
|
|
|
|
Eigen::VectorXi patch_labels(num_patches);
|
|
const int INVALID = std::numeric_limits<int>::max();
|
|
patch_labels.setConstant(INVALID);
|
|
for (size_t i=0; i<num_faces; i++) {
|
|
if (patch_labels[P[i]] == INVALID) {
|
|
patch_labels[P[i]] = labels[i];
|
|
} else {
|
|
assert(patch_labels[P[i]] == labels[i]);
|
|
}
|
|
}
|
|
assert((patch_labels.array() != INVALID).all());
|
|
const size_t num_labels = patch_labels.maxCoeff()+1;
|
|
|
|
Eigen::MatrixXi per_cell_W(num_cells, num_labels);
|
|
per_cell_W.setConstant(INVALID);
|
|
per_cell_W.row(infinity_cell).setZero();
|
|
std::queue<size_t> Q;
|
|
Q.push(infinity_cell);
|
|
while (!Q.empty()) {
|
|
size_t curr_cell = Q.front();
|
|
Q.pop();
|
|
for (const auto& neighbor : cell_adj[curr_cell]) {
|
|
size_t neighbor_cell, patch_idx;
|
|
bool direction;
|
|
std::tie(neighbor_cell, direction, patch_idx) = neighbor;
|
|
if ((per_cell_W.row(neighbor_cell).array() == INVALID).any()) {
|
|
per_cell_W.row(neighbor_cell) = per_cell_W.row(curr_cell);
|
|
for (size_t i=0; i<num_labels; i++) {
|
|
int inc = (patch_labels[patch_idx] == (int)i) ?
|
|
(direction ? -1:1) :0;
|
|
per_cell_W(neighbor_cell, i) =
|
|
per_cell_W(curr_cell, i) + inc;
|
|
}
|
|
Q.push(neighbor_cell);
|
|
} else {
|
|
#ifndef NDEBUG
|
|
// Checking for winding number consistency.
|
|
// This check would inevitably fail for meshes that contain open
|
|
// boundary or non-orientable. However, the inconsistent winding number
|
|
// field would still be useful in some cases such as when problem region
|
|
// is local and embedded within the volume. This, unfortunately, is the
|
|
// best we can do because the problem of computing integer winding
|
|
// number is ill-defined for open and non-orientable surfaces.
|
|
for (size_t i=0; i<num_labels; i++) {
|
|
if ((int)i == patch_labels[patch_idx]) {
|
|
int inc = direction ? -1:1;
|
|
//assert(per_cell_W(neighbor_cell, i) ==
|
|
// per_cell_W(curr_cell, i) + inc);
|
|
} else {
|
|
//assert(per_cell_W(neighbor_cell, i) ==
|
|
// per_cell_W(curr_cell, i));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#ifdef PROPAGATE_WINDING_NUMBER_TIMING
|
|
log_time("propagate_winding_number");
|
|
#endif
|
|
|
|
W.resize(num_faces, num_labels*2);
|
|
for (size_t i=0; i<num_faces; i++)
|
|
{
|
|
const size_t patch = P[i];
|
|
const size_t positive_cell = C(patch, 0);
|
|
const size_t negative_cell = C(patch, 1);
|
|
for (size_t j=0; j<num_labels; j++) {
|
|
W(i,j*2 ) = per_cell_W(positive_cell, j);
|
|
W(i,j*2+1) = per_cell_W(negative_cell, j);
|
|
}
|
|
}
|
|
#ifdef PROPAGATE_WINDING_NUMBER_TIMING
|
|
log_time("store_result");
|
|
#endif
|
|
return valid;
|
|
}
|
|
|
|
|
|
#ifdef IGL_STATIC_LIBRARY
|
|
// Explicit template instantiation
|
|
// generated by autoexplicit.sh
|
|
template bool igl::copyleft::cgal::propagate_winding_numbers<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, unsigned long, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, std::vector<std::vector<unsigned long, std::allocator<unsigned long> >, std::allocator<std::vector<unsigned long, std::allocator<unsigned long> > > > const&, unsigned long, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, unsigned long, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
|
|
// generated by autoexplicit.sh
|
|
template bool igl::copyleft::cgal::propagate_winding_numbers<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, unsigned long, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, std::vector<std::vector<unsigned long, std::allocator<unsigned long> >, std::allocator<std::vector<unsigned long, std::allocator<unsigned long> > > > const&, unsigned long, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, unsigned long, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
|
|
template bool igl::copyleft::cgal::propagate_winding_numbers<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
|
|
#ifdef WIN32
|
|
template bool igl::copyleft::cgal::propagate_winding_numbers<class Eigen::Matrix<class CGAL::Lazy_exact_nt<class CGAL::Gmpq>, -1, -1, 1, -1, -1>, class Eigen::Matrix<int, -1, -1, 0, -1, -1>, class Eigen::Matrix<int, -1, -1, 0, -1, -1>, unsigned __int64, class Eigen::Matrix<int, -1, 1, 0, -1, 1>, class Eigen::Matrix<int, -1, -1, 0, -1, -1>, class Eigen::Matrix<int, -1, 1, 0, -1, 1>, class Eigen::Matrix<int, -1, -1, 0, -1, -1>>(class Eigen::PlainObjectBase<class Eigen::Matrix<class CGAL::Lazy_exact_nt<class CGAL::Gmpq>, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, -1, 0, -1, -1>> const &, class std::vector<class std::vector<unsigned __int64, class std::allocator<unsigned __int64>>, class std::allocator<class std::vector<unsigned __int64, class std::allocator<unsigned __int64>>>> const &, unsigned __int64, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, 1, 0, -1, 1>> const &, unsigned __int64, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, 1, 0, -1, 1>> const &, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, -1, 0, -1, -1>> &);
|
|
template bool igl::copyleft::cgal::propagate_winding_numbers<class Eigen::Matrix<class CGAL::Lazy_exact_nt<class CGAL::Gmpq>, -1, -1, 1, -1, -1>, class Eigen::Matrix<int, -1, 3, 1, -1, 3>, class Eigen::Matrix<int, -1, -1, 0, -1, -1>, unsigned __int64, class Eigen::Matrix<int, -1, 1, 0, -1, 1>, class Eigen::Matrix<int, -1, -1, 0, -1, -1>, class Eigen::Matrix<int, -1, 1, 0, -1, 1>, class Eigen::Matrix<int, -1, -1, 0, -1, -1>>(class Eigen::PlainObjectBase<class Eigen::Matrix<class CGAL::Lazy_exact_nt<class CGAL::Gmpq>, -1, -1, 1, -1, -1>> const &, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, 3, 1, -1, 3>> const &, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, -1, 0, -1, -1>> const &, class std::vector<class std::vector<unsigned __int64, class std::allocator<unsigned __int64>>, class std::allocator<class std::vector<unsigned __int64, class std::allocator<unsigned __int64>>>> const &, unsigned __int64, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, 1, 0, -1, 1>> const &, unsigned __int64, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, -1, 0, -1, -1>> const &, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, 1, 0, -1, 1>> const &, class Eigen::PlainObjectBase<class Eigen::Matrix<int, -1, -1, 0, -1, -1>> &);
|
|
#endif
|
|
#endif
|