ccPolyline* ccContourExtractor::ExtractFlatContour( CCLib::GenericIndexedCloudPersist* points, bool allowMultiPass, PointCoordinateType maxEdgeLength/*=0*/, const PointCoordinateType* preferredNormDim/*=0*/, const PointCoordinateType* preferredUpDir/*=0*/, ContourType contourType/*=FULL*/, std::vector<unsigned>* originalPointIndexes/*=0*/, bool enableVisualDebugMode/*=false*/, double maxAngleDeg/*=0.0*/) { assert(points); if (!points) return 0; unsigned ptsCount = points->size(); if (ptsCount < 3) return 0; CCLib::Neighbourhood Yk(points); CCVector3 O,X,Y; //local base bool useOXYasBase = false; //we project the input points on a plane std::vector<Vertex2D> points2D; PointCoordinateType* planeEq = 0; //if the user has specified a default direction, we'll use it as 'projecting plane' PointCoordinateType preferredPlaneEq[4] = {0, 0, 0, 0}; if (preferredNormDim != 0) { const CCVector3* G = points->getPoint(0); //any point through which the point passes is ok preferredPlaneEq[0] = preferredNormDim[0]; preferredPlaneEq[1] = preferredNormDim[1]; preferredPlaneEq[2] = preferredNormDim[2]; CCVector3::vnormalize(preferredPlaneEq); preferredPlaneEq[3] = CCVector3::vdot(G->u, preferredPlaneEq); planeEq = preferredPlaneEq; if (preferredUpDir != 0) { O = *G; Y = CCVector3(preferredUpDir); X = Y.cross(CCVector3(preferredNormDim)); useOXYasBase = true; } } if (!Yk.projectPointsOn2DPlane<Vertex2D>(points2D, planeEq, &O, &X, &Y, useOXYasBase)) { ccLog::Warning("[ExtractFlatContour] Failed to project the points on the LS plane (not enough memory?)!"); return 0; } //update the points indexes (not done by Neighbourhood::projectPointsOn2DPlane) { for (unsigned i = 0; i < ptsCount; ++i) points2D[i].index = i; } //try to get the points on the convex/concave hull to build the contour and the polygon Hull2D hullPoints; if (!ExtractConcaveHull2D( points2D, hullPoints, contourType, allowMultiPass, maxEdgeLength*maxEdgeLength, enableVisualDebugMode, maxAngleDeg)) { ccLog::Warning("[ExtractFlatContour] Failed to compute the convex hull of the input points!"); return 0; } if (originalPointIndexes) { try { originalPointIndexes->resize(hullPoints.size(), 0); } catch (const std::bad_alloc&) { //not enough memory ccLog::Error("[ExtractFlatContour] Not enough memory!"); return 0; } unsigned i=0; for (Hull2D::const_iterator it = hullPoints.begin(); it != hullPoints.end(); ++it, ++i) (*originalPointIndexes)[i] = (*it)->index; } unsigned hullPtsCount = static_cast<unsigned>(hullPoints.size()); //create vertices ccPointCloud* contourVertices = new ccPointCloud(); { if (!contourVertices->reserve(hullPtsCount)) { delete contourVertices; contourVertices = 0; ccLog::Error("[ExtractFlatContour] Not enough memory!"); return 0; } //projection on the LS plane (in 3D) for (Hull2D::const_iterator it = hullPoints.begin(); it != hullPoints.end(); ++it) contourVertices->addPoint(O + X*(*it)->x + Y*(*it)->y); contourVertices->setName("vertices"); contourVertices->setEnabled(false); } //we create the corresponding (3D) polyline ccPolyline* contourPolyline = new ccPolyline(contourVertices); if (contourPolyline->reserve(hullPtsCount)) { contourPolyline->addPointIndex(0, hullPtsCount); contourPolyline->setClosed(contourType == FULL); contourPolyline->setVisible(true); contourPolyline->setName("contour"); contourPolyline->addChild(contourVertices); } else { delete contourPolyline; contourPolyline = 0; ccLog::Warning("[ExtractFlatContour] Not enough memory to create the contour polyline!"); } return contourPolyline; }
ccPolyline* ccPolyline::ExtractFlatContour( CCLib::GenericIndexedCloudPersist* points, PointCoordinateType maxEdgelLength/*=0*/, const PointCoordinateType* preferredNormDim/*=0*/, const PointCoordinateType* preferredUpDir/*=0*/, ContourType contourType/*=FULL*/, std::vector<unsigned>* originalPointIndexes/*=0*/) { assert(points); if (!points) return 0; unsigned ptsCount = points->size(); if (ptsCount < 3) return 0; CCLib::Neighbourhood Yk(points); CCVector3 O,X,Y; //local base bool useOXYasBase = false; //we project the input points on a plane std::vector<CCLib::PointProjectionTools::IndexedCCVector2> points2D; PointCoordinateType* planeEq = 0; //if the user has specified a default direction, we'll use it as 'projecting plane' PointCoordinateType preferredPlaneEq[4] = {0, 0, 0, 0}; if (preferredNormDim != 0) { const CCVector3* G = points->getPoint(0); //any point through which the point passes is ok preferredPlaneEq[0] = preferredNormDim[0]; preferredPlaneEq[1] = preferredNormDim[1]; preferredPlaneEq[2] = preferredNormDim[2]; CCVector3::vnormalize(preferredPlaneEq); preferredPlaneEq[3] = CCVector3::vdot(G->u,preferredPlaneEq); planeEq = preferredPlaneEq; if (preferredUpDir != 0) { O = *G; Y = CCVector3(preferredUpDir); X = Y.cross(CCVector3(preferredNormDim)); useOXYasBase = true; } } if (!Yk.projectPointsOn2DPlane<CCLib::PointProjectionTools::IndexedCCVector2>(points2D,planeEq,&O,&X,&Y,useOXYasBase)) { ccLog::Warning("[ccPolyline::ExtractFlatContour] Failed to project the points on the LS plane (not enough memory?)!"); return 0; } //update the points indexes (not done by Neighbourhood::projectPointsOn2DPlane) { for (unsigned i=0; i<ptsCount; ++i) points2D[i].index = i; } //try to get the points on the convex/concave hull to build the contour and the polygon Hull2D hullPoints; if (!CCLib::PointProjectionTools::extractConcaveHull2D( points2D, hullPoints, maxEdgelLength*maxEdgelLength) ) { ccLog::Warning("[ccPolyline::ExtractFlatContour] Failed to compute the convex hull of the input points!"); return 0; } bool isClosed = true; if (contourType != FULL) { //look for the min and max 'X' coordinates (preferredNormDim and preferredUpDir should have been defined ;) PointCoordinateType xMin = 0, xMax = 0; { unsigned i=0; for (Hull2D::const_iterator it = hullPoints.begin(); it != hullPoints.end(); ++it, ++i) { const CCLib::PointProjectionTools::IndexedCCVector2* P = *it; if (i != 0) { if (P->x < xMin) xMin = P->x; else if (P->x > xMax) xMax = P->x; } else { xMin = xMax = P->x; } } } if (xMin != xMax) { //now identify the 'min' vertex Hull2D::const_iterator firstIt = hullPoints.end(); { for (Hull2D::const_iterator it = hullPoints.begin(); it != hullPoints.end(); ++it) { const CCLib::PointProjectionTools::IndexedCCVector2* P = *it; if (P->x == xMin) { if ( firstIt == hullPoints.end() //we take the lowest (resp highest) if multiple points with x == xMin || (contourType == LOWER && (*firstIt)->y > P->y) || (contourType == UPPER && (*firstIt)->y < P->y) ) { firstIt = it; } } } } assert(firstIt != hullPoints.end()); //now we are going to keep only the right part try { //determine the right way Hull2D::const_iterator prevIt = (firstIt != hullPoints.begin() ? firstIt : hullPoints.end()); --prevIt; Hull2D::const_iterator nextIt = firstIt; ++nextIt; if (nextIt == hullPoints.end()) nextIt = hullPoints.begin(); bool forward = ( (contourType == LOWER && (*nextIt)->y < (*prevIt)->y) || (contourType == UPPER && (*nextIt)->y > (*prevIt)->y) ); if (!forward) std::swap(prevIt,nextIt); Hull2D hullPart; hullPart.push_back(*firstIt); nextIt = firstIt; while ((*nextIt)->x != xMax) { if (forward) { ++nextIt; if (nextIt == hullPoints.end()) nextIt = hullPoints.begin(); } else { if (nextIt == hullPoints.begin()) nextIt = hullPoints.end(); --nextIt; } hullPart.push_back(*nextIt); } hullPoints = hullPart; isClosed = false; } catch(std::bad_alloc) { ccLog::Error("[ccPolyline::ExtractFlatContour] Not enough memory!"); return 0; } } else //xMin == xMax { //flat contour?! } } if (originalPointIndexes) { try { originalPointIndexes->resize(hullPoints.size(),0); } catch(std::bad_alloc) { //not enough memory ccLog::Error("[ccPolyline::ExtractFlatContour] Not enough memory!"); return 0; } unsigned i=0; for (Hull2D::const_iterator it = hullPoints.begin(); it != hullPoints.end(); ++it, ++i) (*originalPointIndexes)[i] = (*it)->index; } unsigned hullPtsCount = static_cast<unsigned>(hullPoints.size()); //create vertices ccPointCloud* contourVertices = new ccPointCloud(); { if (!contourVertices->reserve(hullPtsCount)) { delete contourVertices; contourVertices = 0; ccLog::Error("[ccPolyline::ExtractFlatContour] Not enough memory!"); return 0; } //projection on the LS plane (in 3D) for (Hull2D::const_iterator it = hullPoints.begin(); it != hullPoints.end(); ++it) contourVertices->addPoint(O + X*(*it)->x + Y*(*it)->y); contourVertices->setName("vertices"); contourVertices->setEnabled(false); } //we create the corresponding (3D) polyline ccPolyline* contourPolyline = new ccPolyline(contourVertices); if (contourPolyline->reserve(hullPtsCount)) { contourPolyline->addPointIndex(0,hullPtsCount); contourPolyline->setClosed(isClosed); contourPolyline->setVisible(true); contourPolyline->setName("contour"); contourPolyline->addChild(contourVertices); } else { delete contourPolyline; contourPolyline = 0; ccLog::Warning("[ccPolyline::ExtractFlatContour] Not enough memory to create the contour polyline!"); } return contourPolyline; }