Commit 420e3b8d authored by Will Schroeder's avatar Will Schroeder
Browse files

Checkpoint

parent f268af10
Pipeline #283033 waiting for manual action with stages
in 0 seconds
......@@ -56,7 +56,8 @@ enum SmoothPointFlag
// from a network of edges (which is typically generated via
// vtkExtractEdges). The stencil, for each point p, is a set of points ps
// connected to p. Here the cell links is used to create the set ps. Also build
// weights associated with each stencil.
// weights associated with each stencil. The weights are multiplied by the
// relaxation factor, it's more efficient to do it now.
struct BuildStencil
{
vtkPoints* Points;
......@@ -66,11 +67,17 @@ struct BuildStencil
vtkIdType* Conn;
const unsigned char* Smooth;
double* Weights;
// Avoid constructing/deleting the iterator
double Relax;
vtkIdType MaxStencilSize;
// Avoid constructing/deleting the cell iterator
vtkSMPThreadLocal<vtkSmartPointer<vtkCellArrayIterator>> Iter;
// Maximum stencil size (for creating final stencils later)
vtkSMPThreadLocal<vtkIdType> MaxSize;
BuildStencil(vtkPoints* pts, vtkCellArray* lines, vtkStaticCellLinksTemplate<vtkIdType>* links,
vtkIdType* offsets, vtkIdType* conn, const unsigned char* smooth, double* weights)
vtkIdType* offsets, vtkIdType* conn, const unsigned char* smooth, double* weights,
double relax)
: Points(pts)
, Lines(lines)
, Links(links)
......@@ -78,14 +85,21 @@ struct BuildStencil
, Conn(conn)
, Smooth(smooth)
, Weights(weights)
, Relax(relax)
, MaxStencilSize(0)
{
}
void Initialize() { this->Iter.Local().TakeReference(this->Lines->NewIterator()); }
void Initialize()
{
this->Iter.Local().TakeReference(this->Lines->NewIterator());
this->MaxSize.Local() = 0;
}
void operator()(vtkIdType ptId, vtkIdType endPtId)
{
vtkCellArrayIterator* iter = this->Iter.Local();
vtkIdType& maxStencilSize = this->MaxSize.Local();
vtkStaticCellLinksTemplate<vtkIdType>* links = this->Links;
vtkIdType npts;
const vtkIdType* pts;
......@@ -102,6 +116,7 @@ struct BuildStencil
double x[3], y[3];
this->Points->GetPoint(ptId,x);
vtkIdType numEdges = links->GetNumberOfCells(ptId);
maxStencilSize = (numEdges > maxStencilSize ? numEdges : maxStencilSize);
vtkIdType* edges = links->GetCells(ptId);
vtkIdType* c = this->Conn + offset;
double* w = this->Weights + offset;
......@@ -125,7 +140,8 @@ struct BuildStencil
}
wSum += w[i];
}
// Normalize final weights
// Normalize the final weights, and multiply by the
// relaxation factor.
if ( forceWeight >= 0 ) // coincident point
{
for (auto j = 0; j < numEdges; ++j)
......@@ -146,14 +162,27 @@ struct BuildStencil
} // for all points in this batch
} // operator()
void Reduce() {}
// Roll up the maximum stencil size. This is used later to
// create stencils and weights.
void Reduce()
{
vtkIdType maxStencilSize = 0;
vtkSMPThreadLocal<vtkIdType>::iterator itr;
vtkSMPThreadLocal<vtkIdType>::iterator itrEnd = this->MaxSize.end();
for (itr = this->MaxSize.begin(); itr != itrEnd; ++itr)
{
maxStencilSize = (*itr > maxStencilSize ? *itr : maxStencilSize);
} // over all threads
this->MaxStencilSize = maxStencilSize;
}
}; // BuildStencil
// Create stencils if none were provided. Leverage the vtkExtractEdges filter
// (which is threaded) to build the stencils.
vtkSmartPointer<vtkCellArray>
BuildStencils(vtkPolyData* edges, const unsigned char *smooth, std::vector<double>& weights)
BuildStencils(vtkPolyData* edges, const unsigned char *smooth,
std::vector<double>& weights, double relax, vtkIdType& maxStencilSize)
{
vtkNew<vtkCellArray> stencils;
......@@ -162,7 +191,7 @@ BuildStencils(vtkPolyData* edges, const unsigned char *smooth, std::vector<doubl
vtkCellArray* lines = edges->GetLines();
vtkIdType numLines = lines->GetNumberOfCells();
// Make sure there is something to process
// Make sure there is something to process.
if (numLines < 1)
{
return stencils;
......@@ -194,8 +223,9 @@ BuildStencils(vtkPolyData* edges, const unsigned char *smooth, std::vector<doubl
// Now point by point build the smoothing stencils
BuildStencil buildStencil(pts, lines, &links, offsetsPtr, connPtr,
smooth, weights.data());
smooth, weights.data(), relax);
vtkSMPTools::For(0, numPts, buildStencil);
maxStencilSize = buildStencil.MaxStencilSize;
// The stencils have been defined, put them in the form of a vtkCellArray and return.
stencils->SetData(offsets, conn);
......@@ -205,23 +235,32 @@ BuildStencils(vtkPolyData* edges, const unsigned char *smooth, std::vector<doubl
// This functor performs a single smoothing iteration over a set of point
// data attributes. Points that are to be smoothed are marked with a non-zero
// Smooth[i] value.
// Smooth[i] value. Note for smoothing to depend on the relaxation factor,
// the smoothing stencil needs to expanded to also include the point being
// smoothed.
struct SmoothAttributes
{
ArrayList* PD;
vtkCellArray* Stencils;
const unsigned char* Smooth;
const double* Weights;
double Relax;
vtkIdType StencilSize;
// Avoid constructing/deleting the iterator
vtkSMPThreadLocal<vtkSmartPointer<vtkCellArrayIterator>> Iter;
// Avoid construction/resizing smoothing stencils and weights
vtkSMPThreadLocal<std::vector<vtkIdType>> StencilPts;
vtkSMPThreadLocal<std::vector<double>> StencilWeights;
SmoothAttributes(vtkCellArray* stencils, const unsigned char *smooth,
const double *weights)
const double *weights, double relax, vtkIdType maxStencilSize)
: PD(nullptr)
, Stencils(stencils)
, Smooth(smooth)
, Weights(weights)
, Relax(relax)
, StencilSize(maxStencilSize)
{
}
......@@ -234,6 +273,8 @@ struct SmoothAttributes
void Initialize()
{
this->Iter.Local().TakeReference(this->Stencils->NewIterator());
this->StencilPts.Local().resize(this->StencilSize+1);
this->StencilWeights.Local().resize(this->StencilSize+1);
}
void operator()(vtkIdType ptId, vtkIdType endPtId)
......@@ -243,12 +284,15 @@ struct SmoothAttributes
vtkIdType npts;
const vtkIdType* pts;
vtkDataArray* offsets = this->Stencils->GetOffsetsArray();
double relax = this->Relax;
// Loop over all points and smooth associated attributes using a
// distance weighted approach.
for (; ptId < endPtId; ++ptId)
{
// Check to see whether this point should be smoothed
// Check to see whether this point should be smoothed. If so,
// complete the stencil by including the current point and its
// associated weight (the relaxation factor).
if ( !this->Smooth || this->Smooth[ptId] == SmoothPoint )
{
iter->GetCellAtId(ptId, npts, pts);
......@@ -256,18 +300,18 @@ struct SmoothAttributes
const double* weights = this->Weights + offset;
this->PD->WeightedAverage(npts, pts, weights, ptId);
}
else //just copy data
else // Otherwise just copy the input data
{
this->PD->Copy(ptId,ptId);
}
} // over all points
} // over all points in this batch
}
void Reduce()
{
}
// Perform a smoothing pass on a pair of point data attributes.
// Perform a threaded smoothing pass on a pair of point data attributes.
void Execute(vtkIdType numPts, ArrayList* attrPair)
{
this->SetSmoothingArrays(attrPair);
......@@ -276,10 +320,11 @@ struct SmoothAttributes
}; // SmoothAttributes
// Mark boundary points
// Mark vtkPolyData boundary points.
void MarkPDBoundary(vtkPolyData* extractedEdges, vtkPolyData* inPolyData,
unsigned char* smooth)
{
// Needed for topological edge neighbor) operations.
inPolyData->BuildLinks();
vtkCellArray *lines = extractedEdges->GetLines();
......@@ -289,24 +334,33 @@ void MarkPDBoundary(vtkPolyData* extractedEdges, vtkPolyData* inPolyData,
const vtkIdType* pts;
vtkNew<vtkIdList> neis;
// Traverse all edges in the dataset and determine if they
// are boundary edges.
for ( auto lineId=0; lineId < numLines; ++lineId )
{
iter->GetCellAtId(lineId, npts, pts);
inPolyData->GetCellEdgeNeighbors((-1),pts[0],pts[1],neis);
if ( neis->GetNumberOfIds() == 1 ) // it's a boundary edge
{
if ( neis->GetNumberOfIds() == 1 )
{ // it's a boundary edge
smooth[pts[0]] = Boundary;
smooth[pts[1]] = Boundary;
}
}
}
} // MarkPDBoundary
// Mark vtkDataSet boundary points. Boundary points are those that
// are used by boundary faces. Boundary faces are determined by
// executing vtkGeometryFilter. Hence there is an implcit assumption
// that the dataset is 3D.
void MarkDSBoundary(vtkPolyData* extractedEdges, vtkDataSet* ds,
unsigned char* smooth)
{
}
} // MarkDSBoundary
// Mark points adjacent to boundary
// Mark all points directly adjacent to the dataset boundary (i.e.,
// points are addjacent when connected by an edge to a boundary point).
// It is assumed that on entry to this function, all points have been
// marked either as NoSmooth or Boundary.
void MarkAdjacent(vtkPolyData* extractedEdges, unsigned char* smooth)
{
vtkCellArray *lines = extractedEdges->GetLines();
......@@ -329,7 +383,7 @@ void MarkAdjacent(vtkPolyData* extractedEdges, unsigned char* smooth)
smooth[pts[0]] = SmoothPoint;
}
}
}
} // MarkAdjacent
} // anonymous namespace
......@@ -416,15 +470,18 @@ int vtkAttributeSmoothingFilter::RequestData(vtkInformation* vtkNotUsed(request)
// Define a smoothing stencil, or use what's provided.
std::vector<double> weights;
double relax = this->RelaxationFactor;
vtkIdType maxStencilSize;
vtkSmartPointer<vtkCellArray> stencils =
BuildStencils(extractedEdges,smooth,weights);
BuildStencils(extractedEdges, smooth, weights, relax, maxStencilSize);
// With the stencil defined, perform the smoothing. Use a double buffering
// approach. Since we are using a threaded algorithm and hence ArrayList, we
// must create multiple instances of ArrayList to smooth to and from the
// appropriate arrays.
int numIter = this->NumberOfIterations, iterNum=1;
SmoothAttributes smoothAttr(stencils, smooth, weights.data());
SmoothAttributes smoothAttr(stencils, smooth, weights.data(), relax,
maxStencilSize);
// The initial smoothing iteration
vtkNew<vtkPointData> initPD;
......
......@@ -18,7 +18,7 @@
*
* vtkAttributeSmoothingFilter is a filter that smooths point attribute data
* using a Laplacian smoothing approach. The effect is to "relax" the
* attributes, reducing high frequency variations. Note that this filter
* attributes, reducing high frequency information. Note that this filter
* operates on all dataset types.
*
* A central concept of this filter is the point smoothing stencil. A
......@@ -50,11 +50,11 @@
* allowed to change (this supports smooth transition of attributes from the
* boundary into the interior of the mesh). Note that the meaning of a
* boundary point (versus interior point) changes depending on the input
* dataset type. For vtkPolyData, boundary *edges* are used; for
* all other dataset types, points used by a boundary *face* are considered
* boundary points. It is also possible to expicitly to specify which points
* are smoothed, and those that are constrained by specifying a smooth mask
* associated with each input point.
* dataset type. For vtkPolyData, boundary *edges* are used to identify
* boundary points; for all other dataset types, points used by a boundary
* *face* are considered boundary points. It is also possible to expicitly
* specify which points are smoothed, and those that are constrained, by
* specifying a smooth mask associated with each input point.
*
* @warning
* Currently the distance weighting function is based on 1/(r**2) weights,
......@@ -68,17 +68,19 @@
* attributes will move towards an "average" value.
*
* @warning
* While this filter will process any dataset type, if the input data is
* a 3D volume, it's likely much faster to use an image-based algorithm.
* While this filter will process any dataset type, if the input data is a 3D
* image volume, it's likely much faster to use an image-based algorithm to
* perform the smoothing.
*
* @warning
* To determine boundary points in vtkPolyData, edges used by only one cell
* are considered boundary (and the associated points defining the edge). To
* determine boundary points for all other cases, a vtkGeometryFilter is used
* to extract the boundary faces - this can be time consuming for large data.
* are considered boundary (and hence the associated points defining the
* edge). To determine boundary points for all other dataset types, a
* vtkGeometryFilter is used to extract the boundary faces - this can be time
* consuming for large data.
*
* @sa
* vtkConstrainedSmoothingFilter vtkExtractEdges
* vtkConstrainedSmoothingFilter vtkExtractEdges vtkGeometryFilter
*/
#ifndef vtkAttributeSmoothingFilter_h
......@@ -119,7 +121,7 @@ public:
* more stable than larger relaxation factors and smaller numbers of
* iterations. The default value is 0.10.
*/
vtkSetMacro(RelaxationFactor, double);
vtkSetClampMacro(RelaxationFactor, double, 0.0, 1.0);
vtkGetMacro(RelaxationFactor, double);
///@}
......@@ -158,12 +160,12 @@ public:
///@{
/**
* Specify a smoothing mask to use (which takes effect only when a
* Specify the smoothing mask to use (which takes effect only when a
* SMOOTHING_MASK smoothing strategy is specified). The smoothing mask is a
* data array with a non-zero value at each point whose attributes are to
* data array with a non-zero value at all points whose attributes are to
* be smoothed. The size of the data array must match the number of imput
* points. If there is a mismatch between the size of the smoothing mask,
* and the number of input points, then a ALL_POINTS smoothing strategy is
* and the number of input points, then an ALL_POINTS smoothing strategy is
* used.
*/
vtkSetSmartPointerMacro(SmoothingMask, vtkDataArray);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment