Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix operations with different numbers of properties #480

Merged
merged 2 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 34 additions & 25 deletions src/manifold/src/boolean_result.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,32 +490,37 @@ void CreateProperties(Manifold::Impl &outR, const VecDH<TriRef> &refPQ,

const int triPQ = refPQ[tri].tri;
const bool PQ = refPQ[tri].meshID == 0;
const int oldNumProp = PQ ? numPropP : numPropQ;
const auto &properties =
PQ ? inP.meshRelation_.properties : inQ.meshRelation_.properties;
const auto &triProp = PQ ? inP.meshRelation_.triProperties[triPQ]
: inQ.meshRelation_.triProperties[triPQ];
const glm::ivec3 &triProp = oldNumProp == 0 ? glm::ivec3(-1)
: PQ ? inP.meshRelation_.triProperties[triPQ]
: inQ.meshRelation_.triProperties[triPQ];

for (const int i : {0, 1, 2}) {
const int vert = outR.halfedge_[3 * tri + i].startVert;
const glm::vec3 uvw = bary[3 * tri + i];

auto key = std::make_tuple(PQ, vert, -1, -1);
int edge = -1;
for (const int j : {0, 1, 2}) {
if (uvw[j] == 1) {
// On a retained vert, the propVert must also match
std::get<2>(key) = triProp[j];
edge = -1;
break;
auto key = std::make_tuple(PQ, -1, -1, -1);
if (oldNumProp > 0) {
std::get<1>(key) = vert;
int edge = -1;
for (const int j : {0, 1, 2}) {
if (uvw[j] == 1) {
// On a retained vert, the propVert must also match
std::get<2>(key) = triProp[j];
edge = -1;
break;
}
if (uvw[j] == 0) edge = j;
}
if (edge >= 0) {
// On an edge, both propVerts must match
const int p0 = triProp[Next3(edge)];
const int p1 = triProp[Prev3(edge)];
std::get<2>(key) = glm::min(p0, p1);
std::get<3>(key) = glm::max(p0, p1);
}
if (uvw[j] == 0) edge = j;
}
if (edge >= 0) {
// On an edge, both propVerts must match
const int p0 = triProp[Next3(edge)];
const int p1 = triProp[Prev3(edge)];
std::get<2>(key) = glm::min(p0, p1);
std::get<3>(key) = glm::max(p0, p1);
}

const auto it = propIdx.find(key);
Expand All @@ -527,10 +532,14 @@ void CreateProperties(Manifold::Impl &outR, const VecDH<TriRef> &refPQ,
propIdx.insert({key, idx++});

for (int p = 0; p < numProp; ++p) {
glm::vec3 oldProps;
for (const int j : {0, 1, 2})
oldProps[j] = properties[numProp * triProp[j] + p];
outR.meshRelation_.properties.push_back(glm::dot(uvw, oldProps));
if (p < oldNumProp) {
glm::vec3 oldProps;
for (const int j : {0, 1, 2})
oldProps[j] = properties[oldNumProp * triProp[j] + p];
outR.meshRelation_.properties.push_back(glm::dot(uvw, oldProps));
} else {
outR.meshRelation_.properties.push_back(0);
}
}
}
}
Expand All @@ -551,12 +560,12 @@ Manifold::Impl Boolean3::Result(OpType op) const {
const int c2 = op == OpType::Add ? 1 : 0;
const int c3 = op == OpType::Intersect ? 1 : -1;

if (w03_.size() == 0) {
if (w30_.size() != 0 && op == OpType::Add) {
if (inP_.IsEmpty()) {
if (!inQ_.IsEmpty() && op == OpType::Add) {
return inQ_;
}
return Manifold::Impl();
} else if (w30_.size() == 0) {
} else if (inQ_.IsEmpty()) {
if (op == OpType::Intersect) {
return Manifold::Impl();
}
Expand Down
69 changes: 57 additions & 12 deletions src/manifold/src/csg_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ struct UpdateHalfedge {
}
};

struct UpdateTriProp {
const int nextProp;

__host__ __device__ glm::ivec3 operator()(glm::ivec3 tri) {
tri += nextProp;
return tri;
}
};

struct UpdateMeshIDs {
const int offset;

Expand Down Expand Up @@ -142,9 +151,12 @@ Manifold::Impl CsgLeafNode::Compose(
int numVert = 0;
int numEdge = 0;
int numTri = 0;
int numPropVert = 0;
std::vector<int> vertIndices;
std::vector<int> edgeIndices;
std::vector<int> triIndices;
std::vector<int> propVertIndices;
int numPropOut = 0;
for (auto &node : nodes) {
float nodeOldScale = node->pImpl_->bBox_.Scale();
float nodeNewScale =
Expand All @@ -158,9 +170,15 @@ Manifold::Impl CsgLeafNode::Compose(
vertIndices.push_back(numVert);
edgeIndices.push_back(numEdge * 2);
triIndices.push_back(numTri);
propVertIndices.push_back(numPropVert);
numVert += node->pImpl_->NumVert();
numEdge += node->pImpl_->NumEdge();
numTri += node->pImpl_->NumTri();
const int numProp = node->pImpl_->NumProp();
numPropOut = glm::max(numPropOut, numProp);
numPropVert +=
numProp == 0 ? 1
: node->pImpl_->meshRelation_.properties.size() / numProp;
}

Manifold::Impl combined;
Expand All @@ -170,6 +188,11 @@ Manifold::Impl CsgLeafNode::Compose(
combined.faceNormal_.resize(numTri);
combined.halfedgeTangent_.resize(2 * numEdge);
combined.meshRelation_.triRef.resize(numTri);
if (numPropOut > 0) {
combined.meshRelation_.numProp = numPropOut;
combined.meshRelation_.properties.resize(numPropOut * numPropVert, 0);
combined.meshRelation_.triProperties.resize(numTri);
}
auto policy = autoPolicy(numTri);

// if we are already parallelizing for each node, do not perform multithreaded
Expand All @@ -182,24 +205,51 @@ Manifold::Impl CsgLeafNode::Compose(
for_each_n_host(
nodes.size() > 1 ? ExecutionPolicy::Par : ExecutionPolicy::Seq,
countAt(0), nodes.size(),
[&nodes, &vertIndices, &edgeIndices, &triIndices, &combined,
policy](int i) {
[&nodes, &vertIndices, &edgeIndices, &triIndices, &propVertIndices,
numPropOut, &combined, policy](int i) {
auto &node = nodes[i];
copy(policy, node->pImpl_->halfedgeTangent_.begin(),
node->pImpl_->halfedgeTangent_.end(),
combined.halfedgeTangent_.begin() + edgeIndices[i]);
transform(
policy, node->pImpl_->halfedge_.begin(),
node->pImpl_->halfedge_.end(),
combined.halfedge_.begin() + edgeIndices[i],
UpdateHalfedge({vertIndices[i], edgeIndices[i], triIndices[i]}));

if (numPropOut > 0) {
auto start =
combined.meshRelation_.triProperties.begin() + triIndices[i];
if (node->pImpl_->NumProp() > 0) {
auto &triProp = node->pImpl_->meshRelation_.triProperties;
transform(policy, triProp.begin(), triProp.end(), start,
UpdateTriProp({propVertIndices[i]}));

const int numProp = node->pImpl_->NumProp();
auto &oldProp = node->pImpl_->meshRelation_.properties;
auto &newProp = combined.meshRelation_.properties;
for (int p = 0; p < numProp; ++p) {
strided_range<VecDH<float>::IterC> oldRange(
oldProp.begin() + p, oldProp.end(), numProp);
strided_range<VecDH<float>::Iter> newRange(
newProp.begin() + numPropOut * propVertIndices[i] + p,
newProp.end(), numPropOut);
copy(policy, oldRange.begin(), oldRange.end(), newRange.begin());
}
} else {
// point all triangles at single new property of zeros.
fill(policy, start, start + node->pImpl_->NumTri(),
glm::ivec3(propVertIndices[i]));
}
}

if (node->transform_ == glm::mat4x3(1.0f)) {
copy(policy, node->pImpl_->vertPos_.begin(),
node->pImpl_->vertPos_.end(),
combined.vertPos_.begin() + vertIndices[i]);
copy(policy, node->pImpl_->faceNormal_.begin(),
node->pImpl_->faceNormal_.end(),
combined.faceNormal_.begin() + triIndices[i]);
transform(
policy, node->pImpl_->halfedge_.begin(),
node->pImpl_->halfedge_.end(),
combined.halfedge_.begin() + edgeIndices[i],
UpdateHalfedge({vertIndices[i], edgeIndices[i], triIndices[i]}));
} else {
// no need to apply the transform to the node, just copy the vertices
// and face normals and apply transform on the fly
Expand All @@ -215,11 +265,6 @@ Manifold::Impl CsgLeafNode::Compose(
copy_n(policy, faceNormalBegin, node->pImpl_->faceNormal_.size(),
combined.faceNormal_.begin() + triIndices[i]);

transform(
policy, node->pImpl_->halfedge_.begin(),
node->pImpl_->halfedge_.end(),
combined.halfedge_.begin() + edgeIndices[i],
UpdateHalfedge({vertIndices[i], edgeIndices[i], triIndices[i]}));
const bool invert = glm::determinant(glm::mat3(node->transform_)) < 0;
for_each_n(policy,
zip(combined.halfedgeTangent_.begin() + edgeIndices[i],
Expand Down
38 changes: 38 additions & 0 deletions test/boolean_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,44 @@ TEST(Boolean, Cubes) {
#endif
}

TEST(Boolean, NoRetainedVerts) {
Manifold cube = Manifold::Cube(glm::vec3(1), true);
Manifold oct = Manifold::Sphere(1, 4);
EXPECT_NEAR(cube.GetProperties().volume, 1, 0.001);
EXPECT_NEAR(oct.GetProperties().volume, 1.333, 0.001);
EXPECT_NEAR((cube ^ oct).GetProperties().volume, 0.833, 0.001);
}

TEST(Boolean, PropertiesNoIntersection) {
MeshGL cubeUV = CubeUV();
Manifold m0(cubeUV);
Manifold m1 = m0.Translate(glm::vec3(1.5));
Manifold result = m0 + m1;
EXPECT_EQ(result.NumProp(), 2);
RelatedGL(result, {cubeUV});
}

TEST(Boolean, MixedProperties) {
MeshGL cubeUV = CubeUV();
Manifold m0(cubeUV);
Manifold m1 = Manifold::Cube();
Manifold result = m0 + m1.Translate(glm::vec3(0.5));
EXPECT_EQ(result.NumProp(), 2);
RelatedGL(result, {cubeUV, m1.GetMeshGL()});
}

TEST(Boolean, MixedNumProp) {
MeshGL cubeUV = CubeUV();
Manifold m0(cubeUV);
Manifold m1 = Manifold::Cube();
Manifold result =
m0 + m1.SetProperties(1, [](float* prop, glm::vec3 p, const float* n) {
prop[0] = 1;
}).Translate(glm::vec3(0.5));
EXPECT_EQ(result.NumProp(), 2);
RelatedGL(result, {cubeUV, m1.GetMeshGL()});
}

TEST(Boolean, Subtract) {
Mesh firstMesh;
firstMesh.vertPos = {{0, 0, 0}, {1540, 0, 0},
Expand Down
1 change: 1 addition & 0 deletions test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ MeshGL CubeSTL();
MeshGL WithIndexColors(const Mesh& in);
MeshGL WithPositionColors(const Manifold& in);
MeshGL WithNormals(const Manifold& in);
MeshGL CubeUV();
float GetMaxProperty(const MeshGL& mesh, int channel);
float GetMinProperty(const MeshGL& mesh, int channel);
void Identical(const Mesh& mesh1, const Mesh& mesh2);
Expand Down
27 changes: 26 additions & 1 deletion test/test_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,31 @@ MeshGL WithNormals(const Manifold& in) {
return out;
}

MeshGL CubeUV() {
MeshGL mgl;
mgl.numProp = 5;
mgl.vertProperties = {0.5, -0.5, 0.5, 0.5, 0.66, //
-0.5, -0.5, 0.5, 0.25, 0.66, //
0.5, 0.5, 0.5, 0.5, 0.33, //
-0.5, 0.5, 0.5, 0.25, 0.33, //
-0.5, -0.5, -0.5, 1.0, 0.66, //
0.5, -0.5, -0.5, 0.75, 0.66, //
-0.5, 0.5, -0.5, 1.0, 0.33, //
0.5, 0.5, -0.5, 0.75, 0.33, //
-0.5, -0.5, -0.5, 0.0, 0.66, //
-0.5, 0.5, -0.5, 0.0, 0.33, //
-0.5, 0.5, -0.5, 0.25, 0.0, //
0.5, 0.5, -0.5, 0.5, 0.0, //
-0.5, -0.5, -0.5, 0.25, 1.0, //
0.5, -0.5, -0.5, 0.5, 1.0};
mgl.triVerts = {3, 1, 0, 3, 0, 2, 7, 5, 4, 7, 4, 6, 2, 0, 5, 2, 5, 7,
9, 8, 1, 9, 1, 3, 11, 10, 3, 11, 3, 2, 0, 1, 12, 0, 12, 13};
mgl.mergeFromVert = {8, 12, 13, 9, 10, 11};
mgl.mergeToVert = {4, 4, 5, 6, 6, 7};
mgl.runOriginalID.push_back(Manifold::ReserveIDs(1));
return mgl;
}

float GetMaxProperty(const MeshGL& mesh, int channel) {
float max = -std::numeric_limits<float>::infinity();
const int numVert = mesh.NumVert();
Expand Down Expand Up @@ -344,7 +369,7 @@ void RelatedGL(const Manifold& out, const std::vector<MeshGL>& originals,
ASSERT_NEAR(glm::length(normal), 1, 0.0001);
ASSERT_GT(glm::dot(normal, outNormal), 0);
} else {
for (int p = 3; p < output.numProp; ++p) {
for (int p = 3; p < inMesh.numProp; ++p) {
const float propOut =
output.vertProperties[vert * output.numProp + p];

Expand Down