When the CategoricalData option of the vtkPointDataToCellData filter is selected then in certain cases there are no cell data arrays created from this filter. Looking at vtkPointDataToCellData.cxx, specifically the else part of the snippet:
// If we aren't dealing with categorical data...
if (!(this->CategoricalData))
{
// ...then we simply provide each point with an equal weight value and
// interpolate.
weight = 1.0 / numPts;
for (ptId=0; ptId < numPts; ptId++)
{
weights[ptId] = weight;
}
outCD->InterpolatePoint(inPD, cellId, cellPts, weights);
}
else
{
// ...otherwise, we populate a histogram from the scalar values at each
// point, and then select the bin with the most elements.
hist.Reset(numPts);
for (ptId=0; ptId < numPts; ptId++)
{
pointId = cellPts->GetId(ptId);
hist.Fill(pointId,
input->GetPointData()->GetScalars()->GetTuple1(pointId));
}
outCD->CopyData(inPD, hist.IndexOfLargestBin(), cellId);
}
It seems like this happens when there is no scalar array specified (I’m seeing this in ParaView). So with this also, what is the intended behavior of this option? Is each point array supposed to have a categorical operation happen on it or is only a single point array, i.e. the scalar array, supposed to apply to all of the point arrays? Just curious on this? I’m not sure I have a preference on this but am just thinking about fully documenting this categorical option.
@acbauer In principle, the CategoricalData option cannot be applicable to an arbitrary data array. It ought to work for integral types, but for floating-point types it doesn’t make much sense to do a voting-based “interpolation”.
Looking at the implementation, this mode works only on the active scalar array. Starting at line vtkPointDataToCellData.cxx:273, we have
if (this->CategoricalData == 1)
{
// If the categorical data flag is enabled, then a) there must be scalars
// to treat as categorical data, and b) the scalars must have one component.
if (!input->GetPointData()->GetScalars())
{
vtkDebugMacro(<<"No input scalars!");
delete [] weights;
return 1;
}
if (input->GetPointData()->GetScalars()->GetNumberOfComponents() != 1)
{
vtkDebugMacro(<<"Input scalars have more than one component! Cannot categorize!");
delete [] weights;
return 1;
}
// Set the scalar to interpolate via nearest neighbor. That way, we won't
// get any false values (for example, a zone 4 cell appearing on the
// boundary of zone 3 and zone 5).
output->GetPointData()->SetCopyAttribute(vtkDataSetAttributes::SCALARS, 2,
vtkDataSetAttributes::INTERPOLATE);
}
That last line enables only copying of the active scalar attribute. This should probably apply to any qualifying integral array, so if you wanted to make it work for all qualifying input point data arrays, this is where you would probably want to change the filter.
I’m wondering what the intent here is though before I go messing around with it. With it not working properly in ParaView I’d like to “fix it” but at this point I don’t know what that even really means since I don’t know the intent for when there are no input scalars.
@TJ_Corona any thoughts? What’s Max Zeyen’s tag? He was working a bit on this as well…
That is confusing. From this snippet, it looks as though the categorical data code path should be unreachable if there is no scalar array:
if (this->CategoricalData == 1)
{
// If the categorical data flag is enabled, then a) there must be scalars
// to treat as categorical data, and b) the scalars must have one component.
if (!input->GetPointData()->GetScalars())
{
vtkDebugMacro(<<"No input scalars!");
delete [] weights;
return 1;
}
if (input->GetPointData()->GetScalars()->GetNumberOfComponents() != 1)
{
vtkDebugMacro(<<"Input scalars have more than one component! Cannot categorize!");
delete [] weights;
return 1;
}
// Set the scalar to interpolate via nearest neighbor. That way, we won't
// get any false values (for example, a zone 4 cell appearing on the
// boundary of zone 3 and zone 5).
output->GetPointData()->SetCopyAttribute(vtkDataSetAttributes::SCALARS, 2,
vtkDataSetAttributes::INTERPOLATE);
}
Perhaps the filter is silently failing in your use case?
Looking at the code, I don’t think this is the case. If floating-point values are within VTK_EPSILON (defined in the source file as 1.e06, then they are treated as equivalent). I’m not saying that’s what the code should do, I’m saying that’s what it does (git blame says some guy named T.J. Corona wrote this in 2016, but I don’t remember what I had yesterday for lunch).
Yeah, that’s what I’m saying about the scalar array issue. If there’s no scalar array then no new cell data arrays are created. ParaView doesn’t work by creating setting a scalar array for array selection (or if you want this filter to operate on all arrays). So this filter in essence is silently failing by producing no requested output which I claim is bad behavior for ParaView.
And yes, it does seem to work with floating-point values as long as they are specified as the scalar array. To me that makes sense. A user could create a double array with the Calculator filter from an integral array and still want to do categorical type operations (users do all kinds of crazy stuff in ParaView since we allow them to!).
I created a default Wavelet, applied a 3D Gradient Filter to it, and performed a PointData2CellData afterwards (state file below). If CategoricalData is set there is no output, even when only processing the scalar data.
How about performing the CategoricalData voting-based interpolation on all PointData with only one component instead of only the active scalar field?
Also it is not clear to me, why selecting CategoricalData doesn’t perform normal interpolation on vector data?