Here are two methods used to sample points on a model (surface mesh or 3D polydata).
Filter connected points
Use BFS (Breadth First Search) algorithm to get connected points in polyData.
std::vector<vtkIdType> BasicMethod::GetCollectConnectedPoints(vtkPolyData* pd, vtkIdType startPointId)
{
std::vector<vtkIdType> collectedPoints;
const int ptsCount = pd->GetNumberOfPoints();
bool *visitedPointIds = new bool[ptsCount];
for( int i = 0; i < ptsCount; ++i )
{
visitedPointIds[i] = false;
}
std::queue<vtkIdType> pointQueue;
// Start from the initial point
pointQueue.push(startPointId);
visitedPointIds[startPointId] = true;
collectedPoints.push_back(startPointId);
while (!pointQueue.empty())
{
vtkIdType currentPointId = pointQueue.front();
pointQueue.pop();
vtkSmartPointer<vtkIdList> cellIds = vtkSmartPointer<vtkIdList>::New();
pd->GetPointCells(currentPointId, cellIds);
for (vtkIdType i = 0; i < cellIds->GetNumberOfIds(); ++i)
{
vtkIdType cellId = cellIds->GetId(i);
vtkSmartPointer<vtkIdList> pointIds = vtkSmartPointer<vtkIdList>::New();
pd->GetCellPoints(cellId, pointIds);
for (vtkIdType j = 0; j < pointIds->GetNumberOfIds(); ++j)
{
vtkIdType pointId = pointIds->GetId(j);
if ( visitedPointIds[pointId] == false )
{
visitedPointIds[pointId] = true;
collectedPoints.push_back(pointId);
pointQueue.push(pointId);
}
}
}
}
delete [] visitedPointIds;
visitedPointIds = nullptr;
return collectedPoints;
}
Iterate through all the points in jumps to achieve the purpose of sampling.
std::vector<vtkIdType> BasicMethod::GetCollectConnectedSamplePts(vtkPolyData* pd, int idOffset)
{
auto ptIds = GetCollectConnectedPoints(pd, 0);
std::vector<vtkIdType> result;
for( int i = 0; i < ptIds.size(); i = i + idOffset )
{
result.push_back( ptIds[i] );
}
return result;
}

Sampling points by spatial distance
Calculate the orientation of the object and create the local coordinate system, and then iteratively query along the three axes to sample a set of spatial distance points.
Related post: https://www.weiy.city/2020/01/explore-the-orientation-of-3d-object/
std::vector<vtkIdType> BasicMethod::GetSpatialSamplePts(vtkPolyData* pd)
{
vtkSPtrNew(pointLocator, vtkPointLocator);
pointLocator->SetDataSet(pd);
pointLocator->BuildLocator();
std::vector<vtkIdType> result;
Point corner, max, mid, min, size;
vtkSPtrNew( obbTree, vtkOBBTree );
obbTree->ComputeOBB( pd, corner.point, max.point, mid.point, min.point, size.point );
Log( IInfo, "corner: (", corner[0], ", ", corner[1], ", ", corner[2], ")" );
Log( IInfo, "max: (", max[0], ", ", max[1], ", ", max[2], ")" );
Log( IInfo, "mid: (", mid[0], ", ", mid[1], ", ", mid[2], ")" );
Log( IInfo, "min: (", min[0], ", ", min[1], ", ", min[2], ")" );
Log( IInfo, "size: (", size[0], ", ", size[1], ", ", size[2], ")" );
Point realSize( max.Length(), mid.Length(), min.Length() );
max.Unit();
mid.Unit();
min.Unit();
Log( IInfo, "realSize: ", realSize[0], ", ", realSize[1], ", ", realSize[2] );
int times = 20;
double xOffset = std::max( realSize[0]*1.0/times, 1e-5 ); // jump zero dimension
double yOffset = std::max( realSize[1]*1.0/times, 1e-5 );
double zOffset = std::max( realSize[2]*1.0/times, 1e-5 );
double maxOffset = std::max( xOffset, yOffset );
maxOffset = std::max( maxOffset, zOffset );
double dis2;
for( int i = 0; i < times; ++i )
{
for( int j = 0; j < times; ++j )
{
for( int k = 0; k < times; ++k )
{
Point pt = corner + xOffset*i*max + yOffset*j*mid + zOffset*k*min;
vtkIdType closedPtId = pointLocator->FindClosestPointWithinRadius(maxOffset, pt.point, dis2);
if(closedPtId > 0)
{
result.push_back( closedPtId );
}
}
}
}
return result;
}