bool ManualSegmentationTools::segmentMeshWitAABox(GenericIndexedMesh* origMesh, GenericIndexedCloudPersist* origVertices, MeshCutterParams& ioParams, GenericProgressCallback* progressCb/*=0*/) { if (!origMesh || !origVertices || origMesh->size() == 0 || origVertices->size() < 3 || ioParams.bbMin.x >= ioParams.bbMax.x || ioParams.bbMin.y >= ioParams.bbMax.y || ioParams.bbMin.z >= ioParams.bbMax.z) { //invalid input parameters return false; } if (origMesh->size() > c_realIndexMask) { //too many triangles! return false; } const double& epsilon = ioParams.epsilon; const CCVector3d& bbMin = ioParams.bbMin; const CCVector3d& bbMax = ioParams.bbMax; //indexes of original triangle that are not modified bt copied "as is" std::vector<unsigned> preservedTrianglesInside1; //insde (1) std::vector<unsigned> preservedTrianglesInside2; //insde (2) std::vector<unsigned> preservedTrianglesOutside; //outside //inside meshes (swapped for each dimension) ChunkedPointCloud* insideVertices1 = new ChunkedPointCloud; SimpleMesh* insideMesh1 = new SimpleMesh(insideVertices1, true); ChunkedPointCloud* insideVertices2 = new ChunkedPointCloud; SimpleMesh* insideMesh2 = new SimpleMesh(insideVertices2, true); //outside mesh (output) ChunkedPointCloud* outsideVertices = 0; SimpleMesh* outsideMesh = 0; if (ioParams.generateOutsideMesh) { outsideVertices = new ChunkedPointCloud; outsideMesh = new SimpleMesh(outsideVertices, true); } //pointers on input and output structures (will change for each dimension) std::vector<unsigned>* preservedTrianglesInside = &preservedTrianglesInside1; std::vector<unsigned>* formerPreservedTriangles = &preservedTrianglesInside2; ChunkedPointCloud* insideVertices = insideVertices1; SimpleMesh* insideMesh = insideMesh1; GenericIndexedMesh* sourceMesh = origMesh; GenericIndexedCloudPersist* sourceVertices = origVertices; CCVector3d boxCenter = (ioParams.bbMin + ioParams.bbMax) / 2; CCVector3d boxHalfSize = (ioParams.bbMax - ioParams.bbMin) / 2; bool error = false; //for each triangle try { //for each plane for (unsigned d = 0; d < 6; ++d) { //Extract the 'plane' information corresponding to the input box faces //-X,+X,-Y,+Y,-Z,+Z unsigned char Z = static_cast<unsigned char>(d / 2); double planeCoord = ((d & 1) ? bbMax : bbMin).u[Z]; bool keepBelow = ((d & 1) ? true : false); assert(preservedTrianglesInside && formerPreservedTriangles); assert(insideVertices && insideMesh); assert(sourceVertices && sourceMesh); s_edgePoint.clear(); std::vector<unsigned> origTriIndexesMapInsideBackup; if (ioParams.trackOrigIndexes) { origTriIndexesMapInsideBackup = ioParams.origTriIndexesMapInside; ioParams.origTriIndexesMapInside.clear(); } //look for original triangles //(the first time they only come from the original mesh but afterwards // they can come from the original mesh through the 'preserved' list // or from the previous 'inside' mesh as we have to test those triangles // against the new plane) unsigned sourceTriCount = sourceMesh ? sourceMesh->size() : 0; //source: previous/original mesh unsigned formerPreservedTriCount = static_cast<unsigned>(formerPreservedTriangles->size()); unsigned triCount = sourceTriCount + formerPreservedTriCount; for (unsigned i = 0; i < triCount; ++i) { bool triangleIsOriginal = false; unsigned souceTriIndex = 0; const VerticesIndexes* tsi = 0; if (i < sourceTriCount) { souceTriIndex = i; triangleIsOriginal = (sourceMesh == origMesh); tsi = sourceMesh->getTriangleVertIndexes(souceTriIndex); } else { souceTriIndex = (*formerPreservedTriangles)[i - sourceTriCount]; triangleIsOriginal = true; tsi = origMesh->getTriangleVertIndexes(souceTriIndex); } //vertices indexes unsigned vertIndexes[3] = { tsi->i1, tsi->i2, tsi->i3 }; if (triangleIsOriginal) { //we flag the vertices indexes as referring to the 'original' mesh vertIndexes[0] |= c_origIndexFlag; vertIndexes[1] |= c_origIndexFlag; vertIndexes[2] |= c_origIndexFlag; } else { //we flag the vertices indexes as referring to the 'source' mesh if ((vertIndexes[0] & c_origIndexFlag) == 0) vertIndexes[0] |= c_srcIndexFlag; if ((vertIndexes[1] & c_origIndexFlag) == 0) vertIndexes[1] |= c_srcIndexFlag; if ((vertIndexes[2] & c_origIndexFlag) == 0) vertIndexes[2] |= c_srcIndexFlag; } //get the vertices (from the right source!) CCVector3d V[3] = { CCVector3d::fromArray(( (vertIndexes[0] & c_origIndexFlag) ? origVertices : sourceVertices)->getPoint(vertIndexes[0] & c_realIndexMask)->u), CCVector3d::fromArray(( (vertIndexes[1] & c_origIndexFlag) ? origVertices : sourceVertices)->getPoint(vertIndexes[1] & c_realIndexMask)->u), CCVector3d::fromArray(( (vertIndexes[2] & c_origIndexFlag) ? origVertices : sourceVertices)->getPoint(vertIndexes[2] & c_realIndexMask)->u) }; if (d == 0) { //perform a triangle-box overlap test the first time! if (!CCMiscTools::TriBoxOverlapd(boxCenter, boxHalfSize, V)) { if (ioParams.generateOutsideMesh) preservedTrianglesOutside.push_back(i); continue; } } //test the position of each vertex relatively to the current plane //char relativePos[3] = { 1, 1, 1 }; //bool insideXY[3] = { false, false, false }; std::vector<unsigned char> insideLocalVertIndexes, outsideLocalVertIndexes; for (unsigned char j = 0; j < 3; ++j) { const CCVector3d& v = V[j]; if (fabs(v.u[Z] - planeCoord) < epsilon) { //relativePos[j] = 0; } else { if (v.u[Z] < planeCoord) { insideLocalVertIndexes.push_back(j); //relativePos[j] = -1; } else { //relativePos is already equal to 1 //relativePos[j] = 1; outsideLocalVertIndexes.push_back(j); } } } //depending on the number of entities on the plane //we'll process the triangles differently bool isFullyInside = false; bool isFullyOutside = false; switch (insideLocalVertIndexes.size() + outsideLocalVertIndexes.size()) { case 0: //all vertices 'in' the plane { //we arbitrarily decide that the triangle is inside! isFullyInside = true; } break; case 1: //2 vertices 'in' the plane { //the triangle is either on one side or another ;) if (insideLocalVertIndexes.empty()) { //the only vertex far from the plane is on the 'otuside' isFullyOutside = true; } else { //the only vertex far from the plane is on the 'inside' isFullyInside = true; } } break; case 2: //1 vertex 'in' the plane { //3 cases: if (insideLocalVertIndexes.empty()) { //the two vertices far from the plane are 'outside' isFullyOutside = true; } else if (outsideLocalVertIndexes.empty()) { //the two vertices far from the plane are 'inside' isFullyInside = true; } else { //the two vertices far from the plane are on both sides //the plane will cut through the edge connecting those two vertices unsigned char iInside = insideLocalVertIndexes.front(); unsigned char iOuside = outsideLocalVertIndexes.front(); unsigned char iCenter = 3 - iInside - iOuside; unsigned iCoutside, iCinside; //we can now create one vertex and two new triangles if (!ComputeEdgePoint( V[iInside], vertIndexes[iInside], V[iOuside], vertIndexes[iOuside], iCoutside, iCinside, planeCoord, Z, outsideVertices, insideVertices) || !AddTriangle( vertIndexes[iCenter], vertIndexes[iInside], keepBelow ? iCinside : iCoutside, keepBelow ? insideMesh : outsideMesh, ((iCenter + 1) % 3) == iInside) || !AddTriangle( vertIndexes[iCenter], vertIndexes[iOuside], keepBelow ? iCoutside : iCinside, keepBelow ? outsideMesh : insideMesh, ((iCenter + 1) % 3) == iOuside)) { //early stop i = triCount; error = true; break; } //remember (origin) source triangle index if (ioParams.trackOrigIndexes) { assert(triangleIsOriginal || souceTriIndex < origTriIndexesMapInsideBackup.size()); unsigned origTriIndex = triangleIsOriginal ? souceTriIndex : origTriIndexesMapInsideBackup[souceTriIndex]; //the source triangle is split in two so each side get one new triangle ioParams.origTriIndexesMapInside.push_back(origTriIndex); if (ioParams.generateOutsideMesh) ioParams.origTriIndexesMapOutside.push_back(origTriIndex); } } } break; case 3: //no vertex 'in' the plane { if (insideLocalVertIndexes.empty()) { //all vertices are 'outside' isFullyOutside = true; } else if (outsideLocalVertIndexes.empty()) { //all vertices are 'inside' isFullyInside = true; } else { //we have one vertex on one side and two on the other side unsigned char iLeft, iRight1, iRight2; bool leftIsInside = true; if (insideLocalVertIndexes.size() == 1) { assert(outsideLocalVertIndexes.size() == 2); iLeft = insideLocalVertIndexes.front(); iRight1 = outsideLocalVertIndexes[0]; iRight2 = outsideLocalVertIndexes[1]; leftIsInside = keepBelow; } else { assert(insideLocalVertIndexes.size() == 2); iLeft = outsideLocalVertIndexes.front(); iRight1 = insideLocalVertIndexes[0]; iRight2 = insideLocalVertIndexes[1]; leftIsInside = !keepBelow; } //the plane cuts through the two edges having the 'single' vertex in common //we are going to create 3 triangles unsigned i1outside, i1inside; unsigned i2outside, i2inside; if ( !ComputeEdgePoint( V[iRight1], vertIndexes[iRight1], V[iLeft], vertIndexes[iLeft], i1outside, i1inside, planeCoord, Z, outsideVertices, insideVertices) || !ComputeEdgePoint( V[iRight2], vertIndexes[iRight2], V[iLeft], vertIndexes[iLeft], i2outside, i2inside, planeCoord, Z, outsideVertices, insideVertices) || !AddTriangle( vertIndexes[iLeft], leftIsInside ? i1inside : i1outside, leftIsInside ? i2inside : i2outside, leftIsInside ? insideMesh : outsideMesh, ((iLeft + 1) % 3) == iRight1) || !AddTriangle( leftIsInside ? i1outside : i1inside, leftIsInside ? i2outside : i2inside, vertIndexes[iRight1], leftIsInside ? outsideMesh : insideMesh, ((iRight2 + 1) % 3) == iRight1) || !AddTriangle( vertIndexes[iRight1], leftIsInside ? i2outside : i2inside, vertIndexes[iRight2], leftIsInside ? outsideMesh : insideMesh, ((iRight2 + 1) % 3) == iRight1) ) { //early stop i = triCount; error = true; break; } //remember (origin) source triangle index if (ioParams.trackOrigIndexes) { assert(triangleIsOriginal || souceTriIndex < origTriIndexesMapInsideBackup.size()); unsigned origTriIndex = triangleIsOriginal ? souceTriIndex : origTriIndexesMapInsideBackup[souceTriIndex]; //each side gets at least one new triangle ioParams.origTriIndexesMapInside.push_back(origTriIndex); if (ioParams.generateOutsideMesh) ioParams.origTriIndexesMapOutside.push_back(origTriIndex); //the third triangle has been added either to the 'inside' or to the 'outside' mesh if (!leftIsInside) ioParams.origTriIndexesMapInside.push_back(origTriIndex); else if (ioParams.generateOutsideMesh) ioParams.origTriIndexesMapOutside.push_back(origTriIndex); } } } break; } if (isFullyInside || isFullyOutside) { //inverted selection? if (!keepBelow) std::swap(isFullyInside, isFullyOutside); if (triangleIsOriginal) { if (isFullyInside) preservedTrianglesInside->push_back(souceTriIndex); else if (ioParams.generateOutsideMesh) preservedTrianglesOutside.push_back(souceTriIndex); } else { //we import the former triangle if (!AddTriangle(vertIndexes[0], vertIndexes[1], vertIndexes[2], isFullyInside ? insideMesh : outsideMesh, true)) { //early stop error = true; break; } if (ioParams.trackOrigIndexes) { assert(souceTriIndex < origTriIndexesMapInsideBackup.size()); unsigned origTriIndex = origTriIndexesMapInsideBackup[souceTriIndex]; if (isFullyInside) ioParams.origTriIndexesMapInside.push_back(origTriIndex); else if (ioParams.generateOutsideMesh) ioParams.origTriIndexesMapOutside.push_back(origTriIndex); } } } } //end for each triangle if ( !ImportSourceVertices(sourceVertices, insideMesh, insideVertices) || (ioParams.generateOutsideMesh && !ImportSourceVertices(sourceVertices, outsideMesh, outsideVertices)) ) { //early stop error = true; break; } if (insideMesh->size() == 0 && preservedTrianglesInside->empty()) { //no triangle inside! break; } if (d < 5) { //clear the source mesh and swap the buffers if (insideMesh == insideMesh1) { assert(sourceMesh == insideMesh2 || sourceMesh == origMesh); insideMesh2->clear(false); insideVertices2->clear(); sourceMesh = insideMesh1; sourceVertices = insideVertices1; insideMesh = insideMesh2; insideVertices = insideVertices2; preservedTrianglesInside2.clear(); preservedTrianglesInside = &preservedTrianglesInside2; formerPreservedTriangles = &preservedTrianglesInside1; } else { assert(sourceMesh == insideMesh1 || sourceMesh == origMesh); insideMesh1->clear(false); insideVertices1->clear(); sourceMesh = insideMesh2; sourceVertices = insideVertices2; insideMesh = insideMesh1; insideVertices = insideVertices1; preservedTrianglesInside1.clear(); preservedTrianglesInside = &preservedTrianglesInside1; formerPreservedTriangles = &preservedTrianglesInside2; } } } //end for each plane //now add the remaining triangles } catch (const std::bad_alloc&) { //not enough memory error = true; } //free some memory s_edgePoint.clear(); formerPreservedTriangles->clear(); if (!error) { //import the 'preserved' (original) triangles if ( !MergeOldTriangles( origMesh, origVertices, insideMesh, insideVertices, *preservedTrianglesInside, ioParams.trackOrigIndexes ? &ioParams.origTriIndexesMapInside : 0) || ( ioParams.generateOutsideMesh && !MergeOldTriangles( origMesh, origVertices, outsideMesh, outsideVertices, preservedTrianglesOutside, ioParams.trackOrigIndexes ? &ioParams.origTriIndexesMapOutside : 0)) ) { error = true; } } if (insideMesh == insideMesh1) { delete insideMesh2; insideMesh2 = 0; insideVertices2 = 0; } else { delete insideMesh1; insideMesh1 = 0; insideVertices1 = 0; } if (error) { delete insideMesh; if (outsideMesh) delete outsideMesh; return false; } if (insideMesh) { insideMesh->resize(insideMesh->size()); } if (outsideMesh) { outsideMesh->resize(outsideMesh->size()); } ioParams.insideMesh = insideMesh; ioParams.outsideMesh = outsideMesh; return true; }
GenericIndexedMesh* ManualSegmentationTools::segmentMesh(GenericIndexedMesh* theMesh, ReferenceCloud* pointIndexes, bool pointsWillBeInside, GenericProgressCallback* progressCb, GenericIndexedCloud* destCloud, unsigned indexShift) { if (!theMesh || !pointIndexes || !pointIndexes->getAssociatedCloud()) return 0; //by default we try a fast process (but with a higher memory consumption) unsigned numberOfPoints = pointIndexes->getAssociatedCloud()->size(); unsigned numberOfIndexes = pointIndexes->size(); //we determine for each point if it is used in the output mesh or not //(and we compute its new index by the way: 0 means that the point is not used, otherwise its index will be newPointIndexes-1) std::vector<unsigned> newPointIndexes; { try { newPointIndexes.resize(numberOfPoints,0); } catch (const std::bad_alloc&) { return 0; //not enough memory } for (unsigned i=0; i<numberOfIndexes; ++i) { assert(pointIndexes->getPointGlobalIndex(i) < numberOfPoints); newPointIndexes[pointIndexes->getPointGlobalIndex(i)] = i+1; } } //negative array for the case where input points are "outside" if (!pointsWillBeInside) { unsigned newIndex = 0; for (unsigned i=0;i<numberOfPoints;++i) newPointIndexes[i] = (newPointIndexes[i] == 0 ? ++newIndex : 0); } //create resulting mesh SimpleMesh* newMesh = 0; { unsigned numberOfTriangles = theMesh->size(); //progress notification NormalizedProgress* nprogress = 0; if (progressCb) { progressCb->reset(); progressCb->setMethodTitle("Extract mesh"); char buffer[256]; sprintf(buffer,"New vertex number: %u",numberOfIndexes); nprogress = new NormalizedProgress(progressCb,numberOfTriangles); progressCb->setInfo(buffer); progressCb->start(); } newMesh = new SimpleMesh(destCloud ? destCloud : pointIndexes->getAssociatedCloud()); unsigned count = 0; theMesh->placeIteratorAtBegining(); for (unsigned i=0; i<numberOfTriangles; ++i) { bool triangleIsOnTheRightSide = true; const VerticesIndexes* tsi = theMesh->getNextTriangleVertIndexes(); //DGM: getNextTriangleVertIndexes is faster for mesh groups! int newVertexIndexes[3]; //VERSION: WE KEEP THE TRIANGLE ONLY IF ITS 3 VERTICES ARE INSIDE for (uchar j=0;j <3; ++j) { const unsigned& currentVertexFlag = newPointIndexes[tsi->i[j]]; //if the vertex is rejected, we discard this triangle if (currentVertexFlag == 0) { triangleIsOnTheRightSide = false; break; } newVertexIndexes[j] = currentVertexFlag-1; } //if we keep the triangle if (triangleIsOnTheRightSide) { if (count == newMesh->size() && !newMesh->reserve(newMesh->size() + 1000)) //auto expand mesh size { //stop process delete newMesh; newMesh = 0; break; } ++count; newMesh->addTriangle( indexShift + newVertexIndexes[0], indexShift + newVertexIndexes[1], indexShift + newVertexIndexes[2] ); } if (nprogress && !nprogress->oneStep()) { //cancel process break; } } if (nprogress) { delete nprogress; nprogress = 0; } if (newMesh) { if (newMesh->size() == 0) { delete newMesh; newMesh = 0; } else if (count < newMesh->size()) { newMesh->resize(count); //should always be ok as count<maxNumberOfTriangles } } } return newMesh; }