Skip to content

Commit

Permalink
Add manifoldness metadata to PolySet
Browse files Browse the repository at this point in the history
  • Loading branch information
kintel committed Mar 5, 2024
1 parent a5e87ac commit 4d291d1
Show file tree
Hide file tree
Showing 14 changed files with 109 additions and 37 deletions.
11 changes: 6 additions & 5 deletions src/core/primitives.cc
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ std::unique_ptr<const Geometry> CubeNode::createGeometry() const
z2 = this->z;
}
int dimension = 3;
auto ps = std::make_unique<PolySet>(3, true);
auto ps = std::make_unique<PolySet>(3, /*convex*/true, /*manifold*/true);
for (int i = 0; i < 8; i++) {
ps->vertices.emplace_back(i & 1 ? x2 : x1, i & 2 ? y2 : y1,
i & 4 ? z2 : z1);
Expand Down Expand Up @@ -194,7 +194,7 @@ std::unique_ptr<const Geometry> SphereNode::createGeometry() const
// if (num_rings % 2 == 0) num_rings++; // To ensure that the middle ring is at
// phi == 0 degrees

auto polyset = std::make_unique<PolySet>(3, true);
auto polyset = std::make_unique<PolySet>(3, /*convex*/true, /*manifold*/true);
polyset->vertices.reserve(num_rings * num_fragments);

// double offset = 0.5 * ((fragments / 2) % 2);
Expand Down Expand Up @@ -408,13 +408,14 @@ std::unique_ptr<const Geometry> PolyhedronNode::createGeometry() const
p->setConvexity(this->convexity);
p->vertices=this->points;
p->indices=this->faces;
p->isTriangular = true;
bool is_triangular = true;
for (auto &poly : p->indices) {
std::reverse(poly.begin(),poly.end());
if (p->isTriangular && poly.size() > 3) {
p->isTriangular = false;
if (is_triangular && poly.size() > 3) {
is_triangular = false;
}
}
p->setTriangular(is_triangular);
return p;
}

Expand Down
2 changes: 1 addition & 1 deletion src/geometry/GeometryEvaluator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ std::shared_ptr<const Geometry> GeometryEvaluator::evaluateGeometry(const Abstra
ps = PolySetUtils::getGeometryAsPolySet(result);
assert(ps && ps->getDimension() == 3);
// We cannot render concave polygons, so tessellate any PolySets
if (!ps->isEmpty() && !ps->isTriangular) {
if (!ps->isEmpty() && !ps->isTriangular()) {
// Since is_convex() doesn't handle non-planar faces, we need to tessellate
// also in the indeterminate state so we cannot just use a boolean comparison. See #1061
bool convex = bool(ps->convexValue()); // bool is true only if tribool is true, (not indeterminate and not false)
Expand Down
46 changes: 37 additions & 9 deletions src/geometry/PolySet.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "printutils.h"
#include "Grid.h"
#include <Eigen/LU>
#include <unordered_set>
#include <utility>

/*! /class PolySet
Expand All @@ -46,7 +47,8 @@
*/

PolySet::PolySet(unsigned int dim, boost::tribool convex) : dim(dim), convex(convex)
PolySet::PolySet(unsigned int dim, boost::tribool convex, boost::tribool manifold)
: dim_(dim), convex_(convex), is_manifold_(manifold)
{
}

Expand All @@ -58,7 +60,7 @@ std::string PolySet::dump() const
{
std::ostringstream out;
out << "PolySet:"
<< "\n dimensions:" << this->dim
<< "\n dimensions:" << dim_
<< "\n convexity:" << this->convexity
<< "\n num polygons: " << indices.size()
<< "\n polygons data:";
Expand All @@ -74,12 +76,12 @@ std::string PolySet::dump() const

BoundingBox PolySet::getBoundingBox() const
{
if (this->bbox.isNull()) {
if (bbox_.isNull()) {
for (const auto& v : vertices) {
this->bbox.extend(v);
bbox_.extend(v);
}
}
return this->bbox;
return bbox_;
}

size_t PolySet::memsize() const
Expand All @@ -102,13 +104,39 @@ void PolySet::transform(const Transform3d& mat)
for (auto& p : this->indices) {
std::reverse(p.begin(), p.end());
}
this->bbox.setNull();
bbox_.setNull();
}

bool PolySet::is_convex() const {
if (convex || this->isEmpty()) return true;
if (!convex) return false;
return PolySetUtils::is_approximately_convex(*this);
if (convex_ || this->isEmpty()) return true;
if (!convex_) return false;
bool is_convex = PolySetUtils::is_approximately_convex(*this);
convex_ = is_convex;
return is_convex;
}

bool PolySet::is_manifold() const {
// FIXME: Check if this PolySet is a valid manifold, set is_manifold_

// FIXME: We only support checking triangular PolySets
if (!is_triangular_ || this->vertices.size() <= 3 || this->indices.size() <= 3) {
is_manifold_ = false;
return false;
}

std::unordered_map<Vector2i, int> edges;
for (const IndexedFace& face : this->indices) {
for (int i=0;i<3;i++) {
Vector2i edge(face[i], face[(i+1)%3]);
if (edge[0] > edge[1]) edge.reverseInPlace();
edges[edge]++;
}
}

bool is_manifold = std::all_of(edges.cbegin(), edges.cend(),
[](const auto &entry) { return entry.second == 2; });
is_manifold_ = is_manifold;
return is_manifold;
}

void PolySet::resize(const Vector3d& newsize, const Eigen::Matrix<bool, 3, 1>& autosize)
Expand Down
24 changes: 16 additions & 8 deletions src/geometry/PolySet.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ class PolySet : public Geometry
PolygonIndices indices;
std::vector<Vector3d> vertices;

PolySet(unsigned int dim, boost::tribool convex = unknown);
PolySet(unsigned int dim, boost::tribool convex = unknown, boost::tribool manifold = unknown);

size_t memsize() const override;
BoundingBox getBoundingBox() const override;
std::string dump() const override;
unsigned int getDimension() const override { return this->dim; }
unsigned int getDimension() const override { return dim_; }
bool isEmpty() const override { return indices.empty(); }
std::unique_ptr<Geometry> copy() const override;

Expand All @@ -33,13 +33,21 @@ class PolySet : public Geometry
void resize(const Vector3d& newsize, const Eigen::Matrix<bool, 3, 1>& autosize) override;

bool is_convex() const;
boost::tribool convexValue() const { return this->convex; }
bool isTriangular = false;
boost::tribool convexValue() const { return convex_; }

bool isTriangular() const { return is_triangular_; }
void setTriangular(bool triangular) { is_triangular_ = triangular; }

bool is_manifold() const;
boost::tribool manifoldValue() const { return is_manifold_; }
void setManifold(bool manifold) const { is_manifold_ = manifold; }

static std::unique_ptr<PolySet> createEmpty() { return PolySet::createEmpty(); }
}

private:
unsigned int dim;
mutable boost::tribool convex;
mutable BoundingBox bbox;
bool is_triangular_ = false;
mutable boost::tribool is_manifold_ = unknown;
unsigned int dim_;
mutable boost::tribool convex_;
mutable BoundingBox bbox_;
};
8 changes: 4 additions & 4 deletions src/geometry/PolySetBuilder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,17 @@ void PolySetBuilder::append(const PolySet& ps)

std::unique_ptr<PolySet> PolySetBuilder::build()
{
std::unique_ptr<PolySet> polyset;
polyset = std::make_unique<PolySet>(dim_, convex_);
auto polyset = std::make_unique<PolySet>(dim_, convex_);
vertices_.copy(std::back_inserter(polyset->vertices));
polyset->indices = std::move(indices_);
polyset->setConvexity(convexity_);
polyset->isTriangular = true;
bool is_triangular = true;
for (const auto& face : polyset->indices) {
if (face.size() > 3) {
polyset->isTriangular = false;
is_triangular = false;
break;
}
}
polyset->setTriangular(is_triangular);
return polyset;
}
5 changes: 3 additions & 2 deletions src/geometry/PolySetUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ std::unique_ptr<Polygon2d> project(const PolySet& ps) {
std::unique_ptr<PolySet> tessellate_faces(const PolySet& polyset)
{
int degeneratePolygons = 0;
// FIXME: Do we maintain manifoldness here?
auto result = std::make_unique<PolySet>(3, polyset.convexValue());
result->setConvexity(polyset.getConvexity());
result->isTriangular = true;
result->setTriangular(true);
// ideally this should not require a copy...
if (polyset.isTriangular) {
if (polyset.isTriangular()) {
result->vertices = polyset.vertices;
result->indices = polyset.indices;
return result;
Expand Down
2 changes: 1 addition & 1 deletion src/geometry/cgal/cgalutils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ std::unique_ptr<PolySet> createPolySetFromNefPolyhedron3(const CGAL::Nef_polyhed
for (const auto& tri : allTriangles) {
polyset->indices.push_back({tri[0], tri[1], tri[2]});
}
polyset->isTriangular = true;
polyset->setTriangular(true);

#if 0 // For debugging
std::cerr.precision(20);
Expand Down
1 change: 1 addition & 0 deletions src/geometry/linalg.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <vector>

using Eigen::Vector2d;
using Eigen::Vector2i;
using Eigen::Vector3d;
using Eigen::Vector3f;
using Eigen::Vector3i;
Expand Down
2 changes: 1 addition & 1 deletion src/geometry/manifold/ManifoldGeometry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ std::string ManifoldGeometry::dump() const {
std::shared_ptr<const PolySet> ManifoldGeometry::toPolySet() const {
manifold::MeshGL mesh = getManifold().GetMeshGL();
auto ps = std::make_shared<PolySet>(3);
ps->isTriangular = true;
ps->setTriangular(true);
ps->vertices.reserve(mesh.NumVert());
ps->indices.reserve(mesh.NumTri());
ps->setConvexity(convexity);
Expand Down
28 changes: 24 additions & 4 deletions src/geometry/manifold/manifoldutils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ const char* statusToString(Error status) {
std::shared_ptr<manifold::Manifold> trustedPolySetToManifold(const PolySet& ps) {
manifold::Mesh mesh;
std::unique_ptr<PolySet> buffer;
if (!ps.isTriangular)
if (!ps.isTriangular())
buffer = PolySetUtils::tessellate_faces(ps);
const PolySet& triangulated = ps.isTriangular ? ps : *buffer;
const PolySet& triangulated = ps.isTriangular() ? ps : *buffer;

auto numfaces = triangulated.indices.size();
const auto &vertices = triangulated.vertices;
Expand Down Expand Up @@ -106,6 +106,26 @@ template std::shared_ptr<const ManifoldGeometry> createManifoldFromSurfaceMesh(c
template std::shared_ptr<const ManifoldGeometry> createManifoldFromSurfaceMesh(const CGAL_DoubleMesh &tm);
#endif

std::unique_ptr<const manifold::Manifold> createManifoldFromTriangularPolySet(const PolySet& ps)
{
assert(ps.isTriangular());

manifold::Mesh mesh;

mesh.vertPos.reserve(ps.vertices.size());
for (const auto& v : ps.vertices) {
mesh.vertPos.emplace_back((float)v.x(), (float)v.y(), (float)v.z());
}

mesh.triVerts.reserve(ps.indices.size());
for (const auto& face : ps.indices) {
assert(face.size() == 3);
mesh.triVerts.emplace_back(face[0], face[1], face[2]);
}

return std::make_unique<manifold::Manifold>(std::move(mesh));
}

std::shared_ptr<const ManifoldGeometry> createManifoldFromPolySet(const PolySet& ps)
{
// FIXME: To create a Manifold object, we may need to first triangulate.
Expand All @@ -117,10 +137,10 @@ std::shared_ptr<const ManifoldGeometry> createManifoldFromPolySet(const PolySet&
// 4. If successfully repaired, try to create Manifold again.

std::unique_ptr<const PolySet> ps_tri;
if (!ps.isTriangular) {
if (!ps.isTriangular()) {
ps_tri = PolySetUtils::tessellate_faces(ps);
}
const PolySet good_ps = ps.isTriangular ? ps : *ps_tri;
const PolySet good_ps = ps.isTriangular() ? ps : *ps_tri;
auto mani = createManifoldFromTriangularPolySet(good_ps);
if (mani->Status() == Error::NoError) {
return std::make_shared<const ManifoldGeometry>(std::shared_ptr<const manifold::Manifold>(std::move(mani)));
Expand Down
4 changes: 3 additions & 1 deletion src/io/export.cc
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,9 @@ struct LexographicLess {

std::unique_ptr<PolySet> createSortedPolySet(const PolySet& ps)
{
auto out = PolySet::createEmpty();
auto out = std::make_unique<PolySet>(ps.getDimension(), ps.convexValue(), ps.manifoldValue());
out->setTriangular(ps.isTriangular());
out->setConvexity(ps.getConvexity());

std::map<Vector3d, int, LexographicLess> vertexMap;

Expand Down
2 changes: 1 addition & 1 deletion src/io/export_stl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ uint64_t append_stl(std::shared_ptr<const PolySet> polyset, std::ostream& output
static_assert(sizeof(float) == 4, "Need 32 bit float");

std::shared_ptr<const PolySet> ps = polyset;
if (!ps->isTriangular) {
if (!ps->isTriangular()) {
ps = PolySetUtils::tessellate_faces(*ps);
}
if (Feature::ExperimentalPredictibleOutput.is_enabled()) {
Expand Down
9 changes: 9 additions & 0 deletions src/utils/hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ std::size_t hash<Vector3d>::operator()(const Vector3d& s) const {
std::size_t hash<Vector3l>::operator()(const Vector3l& s) const {
return Eigen::hash_value(s);
}
std::size_t hash<Vector2i>::operator()(const Vector2i& s) const {
return Eigen::hash_value(s);
}
}

namespace Eigen {
Expand All @@ -32,4 +35,10 @@ size_t hash_value(Eigen::Matrix<int64_t, 3, 1> const& v) {
for (int i = 0; i < 3; ++i) boost::hash_combine(seed, v[i]);
return seed;
}

size_t hash_value(Eigen::Matrix<int32_t, 2, 1> const& v) {
size_t seed = 0;
for (int i = 0; i < 3; ++i) boost::hash_combine(seed, v[i]);
return seed;
}
}
2 changes: 2 additions & 0 deletions src/utils/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ namespace std {
template <> struct hash<Vector3f> { std::size_t operator()(const Vector3f& s) const; };
template <> struct hash<Vector3d> { std::size_t operator()(const Vector3d& s) const; };
template <> struct hash<Vector3l> { std::size_t operator()(const Vector3l& s) const; };
template <> struct hash<Vector2i> { std::size_t operator()(const Vector2i& s) const; };
}

namespace Eigen {
size_t hash_value(Vector3f const& v);
size_t hash_value(Vector3d const& v);
size_t hash_value(Vector3l const& v);
size_t hash_value(Vector2i const& v);
}

0 comments on commit 4d291d1

Please sign in to comment.