307 lines
11 KiB
C
307 lines
11 KiB
C
|
|
/*=========================================================================
|
||
|
|
|
||
|
|
Program: Visualization Toolkit
|
||
|
|
Module: vtkSelectPolyData.h
|
||
|
|
|
||
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
||
|
|
All rights reserved.
|
||
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
||
|
|
|
||
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
||
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||
|
|
PURPOSE. See the above copyright notice for more information.
|
||
|
|
|
||
|
|
=========================================================================*/
|
||
|
|
/**
|
||
|
|
* @class vtkSelectPolyData
|
||
|
|
* @brief select portion of polygonal mesh; generate selection scalars
|
||
|
|
*
|
||
|
|
* vtkSelectPolyData is a filter that selects polygonal data based on
|
||
|
|
* defining a "loop" and indicating the region inside of the loop. The
|
||
|
|
* mesh within the loop consists of complete cells (the cells are not
|
||
|
|
* cut). Alternatively, this filter can be used to generate scalars.
|
||
|
|
* These scalar values, which are a distance measure to the loop, can
|
||
|
|
* be used to clip, contour. or extract data (i.e., anything that an
|
||
|
|
* implicit function can do).
|
||
|
|
*
|
||
|
|
* The loop is defined by an array of x-y-z point coordinates.
|
||
|
|
* (Coordinates should be in the same coordinate space as the input
|
||
|
|
* polygonal data.) The loop can be concave and non-planar, but not
|
||
|
|
* self-intersecting. The input to the filter is a polygonal mesh
|
||
|
|
* (only surface primitives such as triangle strips and polygons); the
|
||
|
|
* output is either a) a portion of the original mesh laying within
|
||
|
|
* the selection loop (GenerateSelectionScalarsOff); or b) the same
|
||
|
|
* polygonal mesh with the addition of scalar values
|
||
|
|
* (GenerateSelectionScalarsOn).
|
||
|
|
*
|
||
|
|
* The algorithm works as follows. For each point coordinate in the
|
||
|
|
* loop, the closest point in the mesh is found. The result is a loop
|
||
|
|
* of closest point ids from the mesh. Then, the edges in the mesh
|
||
|
|
* connecting the closest points (and laying along the lines forming
|
||
|
|
* the loop) are found. Edges between the points can be searched using
|
||
|
|
* one of these methods:
|
||
|
|
* A) Greedy edge tracking.
|
||
|
|
* At the current point, the mesh edge oriented in the
|
||
|
|
* direction of and whose end point is closest to the line is
|
||
|
|
* chosen. The edge is followed to the new end point, and the
|
||
|
|
* procedure is repeated. This process continues until the entire loop
|
||
|
|
* has been created. This method is simple and fast but heuristic,
|
||
|
|
* and edge search can randomly fail ("Can't follow edge" error)
|
||
|
|
* even for simple, flawless meshes when edge search arrives to a point
|
||
|
|
* from where there is no edge pointing towards the next loop point.
|
||
|
|
* B) Dijkstra shortest path. This method guarantees to find the shortest
|
||
|
|
* path between the loop points.
|
||
|
|
*
|
||
|
|
* By default the greedy edge tracking method is used to preserve
|
||
|
|
* backward compatibility, but generally the Dijkstra shortest path
|
||
|
|
* method is recommended due to its robustness.
|
||
|
|
*
|
||
|
|
* To determine what portion of the mesh is inside and outside of the
|
||
|
|
* loop, three options are possible. 1) the smallest connected region,
|
||
|
|
* 2) the largest connected region, and 3) the connected region
|
||
|
|
* closest to a user specified point. (Set the ivar SelectionMode.)
|
||
|
|
*
|
||
|
|
* Once the loop is computed as above, the GenerateSelectionScalars
|
||
|
|
* controls the output of the filter. If on, then scalar values are
|
||
|
|
* generated based on distance to the loop lines. Otherwise, the cells
|
||
|
|
* laying inside the selection loop are output. By default, the mesh
|
||
|
|
* laying within the loop is output; however, if InsideOut is on, then
|
||
|
|
* the portion of the mesh laying outside of the loop is output.
|
||
|
|
*
|
||
|
|
* The filter can be configured to generate the unselected portions of
|
||
|
|
* the mesh as output by setting GenerateUnselectedOutput. Use the
|
||
|
|
* method GetUnselectedOutput to access this output. (Note: this flag
|
||
|
|
* is pertinent only when GenerateSelectionScalars is off.)
|
||
|
|
*
|
||
|
|
* @warning
|
||
|
|
* Make sure that the points you pick are on a connected surface. If
|
||
|
|
* not, then the filter will generate an empty or partial result. Also,
|
||
|
|
* self-intersecting loops will generate unpredictable results.
|
||
|
|
*
|
||
|
|
* @warning
|
||
|
|
* During processing of the data, non-triangular cells are converted to
|
||
|
|
* triangles if GenerateSelectionScalars is off.
|
||
|
|
*
|
||
|
|
* @sa
|
||
|
|
* vtkImplicitSelectionLoop
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifndef vtkSelectPolyData_h
|
||
|
|
#define vtkSelectPolyData_h
|
||
|
|
|
||
|
|
#include "vtkFiltersModelingModule.h" // For export macro
|
||
|
|
#include "vtkPolyDataAlgorithm.h"
|
||
|
|
|
||
|
|
#define VTK_INSIDE_SMALLEST_REGION 0
|
||
|
|
#define VTK_INSIDE_LARGEST_REGION 1
|
||
|
|
#define VTK_INSIDE_CLOSEST_POINT_REGION 2
|
||
|
|
|
||
|
|
#define VTK_GREEDY_EDGE_SEARCH 0
|
||
|
|
#define VTK_DIJKSTRA_EDGE_SEARCH 1
|
||
|
|
|
||
|
|
class vtkCharArray;
|
||
|
|
class vtkPoints;
|
||
|
|
class vtkIdList;
|
||
|
|
|
||
|
|
class VTKFILTERSMODELING_EXPORT vtkSelectPolyData : public vtkPolyDataAlgorithm
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
/**
|
||
|
|
* Instantiate object with InsideOut turned off, and
|
||
|
|
* GenerateSelectionScalars turned off. The unselected output
|
||
|
|
* is not generated, and the inside mode is the smallest region.
|
||
|
|
*/
|
||
|
|
static vtkSelectPolyData* New();
|
||
|
|
|
||
|
|
vtkTypeMacro(vtkSelectPolyData, vtkPolyDataAlgorithm);
|
||
|
|
void PrintSelf(ostream& os, vtkIndent indent) override;
|
||
|
|
|
||
|
|
///@{
|
||
|
|
/**
|
||
|
|
* Set/Get the flag to control behavior of the filter. If
|
||
|
|
* GenerateSelectionScalars is on, then the output of the filter
|
||
|
|
* is the same as the input, except that scalars are generated.
|
||
|
|
* If off, the filter outputs the cells laying inside the loop, and
|
||
|
|
* does not generate scalars.
|
||
|
|
*/
|
||
|
|
vtkSetMacro(GenerateSelectionScalars, vtkTypeBool);
|
||
|
|
vtkGetMacro(GenerateSelectionScalars, vtkTypeBool);
|
||
|
|
vtkBooleanMacro(GenerateSelectionScalars, vtkTypeBool);
|
||
|
|
///@}
|
||
|
|
|
||
|
|
///@{
|
||
|
|
/**
|
||
|
|
* Set/Get the InsideOut flag. When off, the mesh within the loop is
|
||
|
|
* extracted. When on, the mesh outside the loop is extracted.
|
||
|
|
*/
|
||
|
|
vtkSetMacro(InsideOut, vtkTypeBool);
|
||
|
|
vtkGetMacro(InsideOut, vtkTypeBool);
|
||
|
|
vtkBooleanMacro(InsideOut, vtkTypeBool);
|
||
|
|
///@}
|
||
|
|
|
||
|
|
///@{
|
||
|
|
/**
|
||
|
|
* Set the edge search mode. VTK_GREEDY_EDGE_SEARCH is simple and fast,
|
||
|
|
* VTK_DIJKSTRA_EDGE_SEARCH is more robust and guaranteed to provide
|
||
|
|
* shortest path between loop points.
|
||
|
|
* If the algorithm fails with "Can't follow edge" error then switch to
|
||
|
|
* Dijkstra method.
|
||
|
|
* The default is VTK_GREEDY_EDGE_SEARCH for backward compatibility.
|
||
|
|
*/
|
||
|
|
vtkSetClampMacro(EdgeSearchMode, int, VTK_GREEDY_EDGE_SEARCH, VTK_DIJKSTRA_EDGE_SEARCH);
|
||
|
|
vtkGetMacro(EdgeSearchMode, int);
|
||
|
|
void SetEdgeSearchModeToGreedy() { this->SetEdgeSearchMode(VTK_GREEDY_EDGE_SEARCH); }
|
||
|
|
void SetEdgeSearchModeToDijkstra() { this->SetEdgeSearchMode(VTK_DIJKSTRA_EDGE_SEARCH); }
|
||
|
|
const char* GetEdgeSearchModeAsString();
|
||
|
|
///@}
|
||
|
|
|
||
|
|
///@{
|
||
|
|
/**
|
||
|
|
* Set/Get the array of point coordinates defining the loop. There must
|
||
|
|
* be at least three points used to define a loop.
|
||
|
|
*/
|
||
|
|
virtual void SetLoop(vtkPoints*);
|
||
|
|
vtkGetObjectMacro(Loop, vtkPoints);
|
||
|
|
///@}
|
||
|
|
|
||
|
|
///@{
|
||
|
|
/**
|
||
|
|
* Set/Get the point used in SelectionModeToClosestPointRegion.
|
||
|
|
*/
|
||
|
|
vtkSetVector3Macro(ClosestPoint, double);
|
||
|
|
vtkGetVector3Macro(ClosestPoint, double);
|
||
|
|
///@}
|
||
|
|
|
||
|
|
///@{
|
||
|
|
/**
|
||
|
|
* Control how inside/outside of loop is defined.
|
||
|
|
*/
|
||
|
|
vtkSetClampMacro(SelectionMode, int, VTK_INSIDE_SMALLEST_REGION, VTK_INSIDE_CLOSEST_POINT_REGION);
|
||
|
|
vtkGetMacro(SelectionMode, int);
|
||
|
|
void SetSelectionModeToSmallestRegion() { this->SetSelectionMode(VTK_INSIDE_SMALLEST_REGION); }
|
||
|
|
void SetSelectionModeToLargestRegion() { this->SetSelectionMode(VTK_INSIDE_LARGEST_REGION); }
|
||
|
|
void SetSelectionModeToClosestPointRegion()
|
||
|
|
{
|
||
|
|
this->SetSelectionMode(VTK_INSIDE_CLOSEST_POINT_REGION);
|
||
|
|
}
|
||
|
|
const char* GetSelectionModeAsString();
|
||
|
|
///@}
|
||
|
|
|
||
|
|
///@{
|
||
|
|
/**
|
||
|
|
* Control whether a second output is generated. The second output
|
||
|
|
* contains the polygonal data that's not been selected.
|
||
|
|
*/
|
||
|
|
vtkSetMacro(GenerateUnselectedOutput, vtkTypeBool);
|
||
|
|
vtkGetMacro(GenerateUnselectedOutput, vtkTypeBool);
|
||
|
|
vtkBooleanMacro(GenerateUnselectedOutput, vtkTypeBool);
|
||
|
|
///@}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Return output that hasn't been selected (if GenreateUnselectedOutput is
|
||
|
|
* enabled).
|
||
|
|
*/
|
||
|
|
vtkPolyData* GetUnselectedOutput();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Return output port that hasn't been selected (if GenreateUnselectedOutput is
|
||
|
|
* enabled).
|
||
|
|
*/
|
||
|
|
vtkAlgorithmOutput* GetUnselectedOutputPort();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Return the (mesh) edges of the selection region.
|
||
|
|
*/
|
||
|
|
vtkPolyData* GetSelectionEdges();
|
||
|
|
|
||
|
|
// Overload GetMTime() because we depend on Loop
|
||
|
|
vtkMTimeType GetMTime() override;
|
||
|
|
|
||
|
|
protected:
|
||
|
|
vtkSelectPolyData();
|
||
|
|
~vtkSelectPolyData() override;
|
||
|
|
|
||
|
|
int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
|
||
|
|
|
||
|
|
// Compute point list that forms a continuous loop overy the mesh,
|
||
|
|
void GreedyEdgeSearch(vtkPolyData* mesh, vtkIdList* edgeIds);
|
||
|
|
void DijkstraEdgeSearch(vtkPolyData* mesh, vtkIdList* edgeIds);
|
||
|
|
|
||
|
|
// Returns maximum front cell ID
|
||
|
|
vtkIdType ComputeTopologicalDistance(
|
||
|
|
vtkPolyData* mesh, vtkIdList* edgeIds, vtkIntArray* pointMarks, vtkIntArray* cellMarks);
|
||
|
|
|
||
|
|
// Get closest cell to a position that is not at the boundary
|
||
|
|
vtkIdType GetClosestCellId(vtkPolyData* mesh, vtkIntArray* pointMarks);
|
||
|
|
|
||
|
|
// Starting from maxFrontCell, without crossing the boundary, set all cell and point marks to -1.
|
||
|
|
void FillMarksInRegion(vtkPolyData* mesh, vtkIdList* edgePointIds, vtkIntArray* pointMarks,
|
||
|
|
vtkIntArray* cellMarks, vtkIdType cellIdInSelectedRegion);
|
||
|
|
|
||
|
|
void SetClippedResultToOutput(vtkPointData* originalPointData, vtkPolyData* mesh,
|
||
|
|
vtkIntArray* cellMarks, vtkPolyData* output);
|
||
|
|
|
||
|
|
void SetSelectionScalarsToOutput(vtkPointData* originalPointData, vtkCellData* originalCellData,
|
||
|
|
vtkPolyData* mesh, vtkIdList* edgeIds, vtkIntArray* pointMarks, vtkPolyData* output);
|
||
|
|
|
||
|
|
vtkTypeBool GenerateSelectionScalars;
|
||
|
|
vtkTypeBool InsideOut;
|
||
|
|
int EdgeSearchMode;
|
||
|
|
vtkPoints* Loop;
|
||
|
|
int SelectionMode;
|
||
|
|
double ClosestPoint[3];
|
||
|
|
vtkTypeBool GenerateUnselectedOutput;
|
||
|
|
|
||
|
|
private:
|
||
|
|
static void GetPointNeighbors(vtkPolyData* mesh, vtkIdType ptId, vtkIdList* nei);
|
||
|
|
|
||
|
|
// Helper function to check if the edge between pointId1 and pointId2 is present in the
|
||
|
|
// edgePointIds (as direct neighbors).
|
||
|
|
static bool IsBoundaryEdge(vtkIdType pointId1, vtkIdType pointId2, vtkIdList* edgePointIds);
|
||
|
|
|
||
|
|
private:
|
||
|
|
vtkSelectPolyData(const vtkSelectPolyData&) = delete;
|
||
|
|
void operator=(const vtkSelectPolyData&) = delete;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Return the method of determining in/out of loop as a string.
|
||
|
|
*/
|
||
|
|
inline const char* vtkSelectPolyData::GetSelectionModeAsString()
|
||
|
|
{
|
||
|
|
if (this->SelectionMode == VTK_INSIDE_SMALLEST_REGION)
|
||
|
|
{
|
||
|
|
return "InsideSmallestRegion";
|
||
|
|
}
|
||
|
|
else if (this->SelectionMode == VTK_INSIDE_LARGEST_REGION)
|
||
|
|
{
|
||
|
|
return "InsideLargestRegion";
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return "InsideClosestPointRegion";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Return the edge search mode as a string.
|
||
|
|
*/
|
||
|
|
inline const char* vtkSelectPolyData::GetEdgeSearchModeAsString()
|
||
|
|
{
|
||
|
|
if (this->EdgeSearchMode == VTK_GREEDY_EDGE_SEARCH)
|
||
|
|
{
|
||
|
|
return "GreedyEdgeSearch";
|
||
|
|
}
|
||
|
|
else if (this->EdgeSearchMode == VTK_DIJKSTRA_EDGE_SEARCH)
|
||
|
|
{
|
||
|
|
return "DijkstraEdgeSearch";
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// This should never occur
|
||
|
|
return "Invalid";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|