Skip to content

Commit be8f53d

Browse files
authored
added global coplanarity check (elalish#408)
* added global coplanarity check * fix early-out * added test, fixed precision * one more precision
1 parent b30ca18 commit be8f53d

File tree

8 files changed

+71
-11
lines changed

8 files changed

+71
-11
lines changed

.vscode/settings.json

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
"editor.detectIndentation": false,
121121
"cSpell.words": [
122122
"Bary",
123+
"Coplanarity",
123124
"csaszar",
124125
"Gyroid",
125126
"halfedge",

src/manifold/src/impl.cpp

+45-3
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,36 @@ struct CoplanarEdge {
274274
}
275275
};
276276

277+
struct CheckCoplanarity {
278+
int* comp2tri;
279+
const Halfedge* halfedge;
280+
const glm::vec3* vertPos;
281+
const int* components;
282+
const float precision;
283+
284+
__host__ __device__ void operator()(int tri) {
285+
const int component = components[tri];
286+
const int referenceTri = comp2tri[component];
287+
if (referenceTri < 0 || referenceTri == tri) return;
288+
289+
const glm::vec3 origin = vertPos[halfedge[3 * referenceTri].startVert];
290+
const glm::vec3 normal = glm::normalize(
291+
glm::cross(vertPos[halfedge[3 * referenceTri + 1].startVert] - origin,
292+
vertPos[halfedge[3 * referenceTri + 2].startVert] - origin));
293+
294+
for (const int i : {0, 1, 2}) {
295+
const glm::vec3 vert = vertPos[halfedge[3 * tri + i].startVert];
296+
// If any component vertex is not coplanar with the component's reference
297+
// triangle, unmark the entire component so that none of its triangles are
298+
// marked coplanar.
299+
if (glm::abs(glm::dot(normal, vert - origin)) > precision) {
300+
comp2tri[component] = -1;
301+
break;
302+
}
303+
}
304+
}
305+
};
306+
277307
struct EdgeBox {
278308
const glm::vec3* vertPos;
279309

@@ -326,6 +356,7 @@ uint32_t Manifold::Impl::ReserveIDs(uint32_t n) {
326356
Manifold::Impl::Impl(const MeshGL& meshGL,
327357
std::vector<float> propertyTolerance) {
328358
Mesh mesh;
359+
mesh.precision = meshGL.precision;
329360
const int numVert = meshGL.NumVert();
330361
const int numTri = meshGL.NumTri();
331362

@@ -472,7 +503,7 @@ Manifold::Impl::Impl(const Mesh& mesh, const MeshRelationD& relation,
472503
MarkFailure(Error::NonFiniteVertex);
473504
return;
474505
}
475-
SetPrecision();
506+
SetPrecision(mesh.precision);
476507

477508
CreateHalfedges(triVerts);
478509
if (!IsManifold()) {
@@ -609,9 +640,20 @@ void Manifold::Impl::CreateFaces(const std::vector<float>& propertyTolerance) {
609640
}
610641
}
611642

643+
VecDH<int> componentsD(components);
644+
VecDH<int> comp2triD(comp2tri);
645+
for_each_n(
646+
autoPolicy(halfedge_.size()), countAt(0), NumTri(),
647+
CheckCoplanarity({comp2triD.ptrD(), halfedge_.cptrD(), vertPos_.cptrD(),
648+
componentsD.cptrD(), precision_}));
649+
612650
VecDH<TriRef>& triRef = meshRelation_.triRef;
613-
for (int tri = 0; tri < NumTri(); ++tri)
614-
triRef[tri].tri = comp2tri[components[tri]];
651+
for (int tri = 0; tri < NumTri(); ++tri) {
652+
const int referenceTri = comp2triD[components[tri]];
653+
if (referenceTri >= 0) {
654+
triRef[tri].tri = referenceTri;
655+
}
656+
}
615657
}
616658

617659
/**

src/manifold/src/manifold.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ Mesh Manifold::GetMesh() const {
137137
const Impl& impl = *GetCsgLeafNode().GetImpl();
138138

139139
Mesh result;
140+
result.precision = Precision();
140141
result.vertPos.insert(result.vertPos.end(), impl.vertPos_.begin(),
141142
impl.vertPos_.end());
142143
result.vertNormal.insert(result.vertNormal.end(), impl.vertNormal_.begin(),
@@ -178,6 +179,7 @@ MeshGL Manifold::GetMeshGL(glm::ivec3 normalIdx) const {
178179
!isOriginal && glm::all(glm::greaterThan(normalIdx, glm::ivec3(2)));
179180

180181
MeshGL out;
182+
out.precision = Precision();
181183
out.numProp = 3 + numProp;
182184
out.triVerts.resize(3 * numTri);
183185

@@ -379,10 +381,7 @@ int Manifold::Genus() const {
379381
}
380382

381383
/**
382-
* Returns the surface area and volume of the manifold. These properties are
383-
* clamped to zero for a given face if they are within the Precision(). This
384-
* means degenerate manifolds can by identified by testing these properties as
385-
* == 0.
384+
* Returns the surface area and volume of the manifold.
386385
*/
387386
Properties Manifold::GetProperties() const {
388387
return GetCsgLeafNode().GetImpl()->GetProperties();

src/manifold/src/properties.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ struct FaceAreaVolume {
4343
float area = glm::length(crossP);
4444
float volume = glm::dot(crossP, vertPos[halfedges[3 * face].startVert]);
4545

46-
return area > perimeter * precision
47-
? thrust::make_pair(area / 2.0f, volume / 6.0f)
48-
: thrust::make_pair(0.0f, 0.0f);
46+
return thrust::make_pair(area / 2.0f, volume / 6.0f);
4947
}
5048
};
5149

src/manifold/src/sort.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ void Manifold::Impl::GatherFaces(const Impl& old,
480480
/// Constructs a position-only MeshGL from the input Mesh.
481481
MeshGL::MeshGL(const Mesh& mesh) {
482482
numProp = 3;
483+
precision = mesh.precision;
483484
vertProperties.resize(numProp * mesh.vertPos.size());
484485
for (int i = 0; i < mesh.vertPos.size(); ++i) {
485486
for (int j : {0, 1, 2}) vertProperties[3 * i + j] = mesh.vertPos[i][j];

src/utilities/include/public.h

+5
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ struct Mesh {
145145
/// as 3 * tri + i, representing the tangent from Mesh.triVerts[tri][i] along
146146
/// the CCW edge. If empty, mesh is faceted.
147147
std::vector<glm::vec4> halfedgeTangent;
148+
/// The absolute precision of the vertex positions, based on accrued rounding
149+
/// errors. When creating a Manifold, the precision used will be the maximum
150+
/// of this and a baseline precision from the size of the bounding box. Any
151+
/// edge shorter than precision may be collapsed.
152+
float precision = 0;
148153
};
149154

150155
/**

test/cross_section_test.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ TEST(CrossSection, RoundOffset) {
6161
#endif
6262

6363
EXPECT_EQ(result.Genus(), 0);
64-
EXPECT_NEAR(result.GetProperties().volume, 4373, 1);
64+
EXPECT_NEAR(result.GetProperties().volume, 4393, 1);
6565
}
6666

6767
TEST(CrossSection, Empty) {

test/manifold_test.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,20 @@ TEST(Manifold, Precision2) {
362362
EXPECT_FLOAT_EQ(cube.Precision(), 2 * kTolerance);
363363
}
364364

365+
TEST(Manifold, Precision3) {
366+
Manifold cylinder = Manifold::Cylinder(1, 1, 1, 1000);
367+
const auto prop = cylinder.GetProperties();
368+
369+
MeshGL mesh = cylinder.GetMeshGL();
370+
mesh.precision = 0.001;
371+
mesh.faceID.clear();
372+
Manifold cylinder2(mesh);
373+
374+
const auto prop2 = cylinder2.GetProperties();
375+
EXPECT_NEAR(prop.volume, prop2.volume, 0.001);
376+
EXPECT_NEAR(prop.surfaceArea, prop2.surfaceArea, 0.001);
377+
}
378+
365379
/**
366380
* Curvature is the inverse of the radius of curvature, and signed such that
367381
* positive is convex and negative is concave. There are two orthogonal

0 commit comments

Comments
 (0)