// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-FileCopyrightText: Copyright 2003 Sandia Corporation
// SPDX-License-Identifier: LicenseRef-BSD-3-Clause-Sandia-USGov
/**
* @class vtkStreamingTessellator
* @brief An algorithm that refines an initial simplicial tessellation using edge subdivision
*
* This class is a simple algorithm that takes a single starting simplex -- a
* tetrahedron, triangle, or line segment -- and calls a function you
* pass it with (possibly many times) tetrahedra, triangles, or lines
* adaptively sampled from the one you specified. It
* uses an algorithm you specify to control the level of adaptivity.
*
* This class does not create vtkUnstructuredGrid output because it is
* intended for use in mappers as well as filters. Instead, it
* calls the registered function with simplices as they are
* created.
*
* The subdivision algorithm should change the vertex
* coordinates (it must change both geometric and, if desired, parametric
* coordinates) of the midpoint. These coordinates need not be
* changed unless the EvaluateLocationAndFields() member returns true.
* The vtkStreamingTessellator itself has no way of creating
* a more accurate midpoint vertex.
*
* Here's how to use this class:
* - Call AdaptivelySample1Facet, AdaptivelySample2Facet, or
* AdaptivelySample3Facet, with an edge, triangle, or
* tetrahedron you want tessellated.
* - The adaptive tessellator classifies each edge by passing
* the midpoint values to the vtkEdgeSubdivisionCriterion.
* - After each edge is classified, the tessellator subdivides
* edges as required until the subdivision criterion is
* satisfied or the maximum subdivision depth has been
* reached.
* - Edges, triangles, or tetrahedra connecting the vertices
* generated by the subdivision algorithm are processed by
* calling the user-defined callback functions (set with
* SetTetrahedronCallback(), SetTriangleCallback(),
* or SetEdgeCallback() ).
*
* @warning
* Note that the vertices passed to AdaptivelySample3Facet, AdaptivelySample2Facet,
* or AdaptivelySample1Facet must be at least 6, 5, or 4 entries long, respectively!
* This is because the <r,s,t>, <r,s>, or <r>
* parametric coordinates of the vertices are maintained as the
* facet is subdivided. This information is often
* required by the subdivision algorithm in order to compute
* an error metric. You may change the number of parametric coordinates
* associated with each vertex using vtkStreamingTessellator::SetEmbeddingDimension().
*
* @par Interpolating Field Values:
* If you wish, you may also use \p vtkStreamingTessellator to interpolate field
* values at newly created vertices. Interpolated field values are stored just beyond
* the parametric coordinates associated with a vertex. They will always be \p double
* values; it does not make sense to interpolate a boolean or string value and your
* output and subdivision subroutines may always cast to a \p float or use \p floor() to
* truncate an interpolated value to an integer.
*
* @sa
* vtkEdgeSubdivisionCriterion
*/
#ifndef vtkStreamingTessellator_h
#define vtkStreamingTessellator_h
#include "vtkFiltersCoreModule.h" // For export macro
#include "vtkObject.h"
#undef PARAVIEW_DEBUG_TESSELLATOR
VTK_ABI_NAMESPACE_BEGIN
class vtkEdgeSubdivisionCriterion;
class VTKFILTERSCORE_EXPORT vtkStreamingTessellator : public vtkObject
{
public:
vtkTypeMacro(vtkStreamingTessellator, vtkObject);
static vtkStreamingTessellator* New();
void PrintSelf(ostream& os, vtkIndent indent) override;
typedef void (*VertexProcessorFunction)(
const double*, vtkEdgeSubdivisionCriterion*, void*, const void*);
typedef void (*EdgeProcessorFunction)(
const double*, const double*, vtkEdgeSubdivisionCriterion*, void*, const void*);
typedef void (*TriangleProcessorFunction)(
const double*, const double*, const double*, vtkEdgeSubdivisionCriterion*, void*, const void*);
typedef void (*TetrahedronProcessorFunction)(const double*, const double*, const double*,
const double*, vtkEdgeSubdivisionCriterion*, void*, const void*);
enum
{
MaxFieldSize = 18
};
///@{
/**
* Get/Set the function called for each output tetrahedron (3-facet).
*/
virtual void SetTetrahedronCallback(TetrahedronProcessorFunction);
virtual TetrahedronProcessorFunction GetTetrahedronCallback() const;
///@}
///@{
/**
* Get/Set the function called for each output triangle (2-facet).
*/
virtual void SetTriangleCallback(TriangleProcessorFunction);
virtual TriangleProcessorFunction GetTriangleCallback() const;
///@}
///@{
/**
* Get/Set the function called for each output line segment (1-facet).
*/
virtual void SetEdgeCallback(EdgeProcessorFunction);
virtual EdgeProcessorFunction GetEdgeCallback() const;
///@}
///@{
/**
* Get/Set the function called for each output line segment (1-facet).
*/
virtual void SetVertexCallback(VertexProcessorFunction);
virtual VertexProcessorFunction GetVertexCallback() const;
///@}
///@{
/**
* Get/Set a void pointer passed to the triangle and edge output functions.
*/
virtual void SetPrivateData(void* Private);
virtual void* GetPrivateData() const;
///@}
// can't wrap const private data because python wrapper will try to cast it to void*, not const
// void*
///@{
/**
* Get/Set a constant void pointer passed to the simplex output functions.
*/
virtual void SetConstPrivateData(const void* ConstPrivate);
virtual const void* GetConstPrivateData() const;
///@}
///@{
/**
* Get/Set the algorithm used to determine whether an edge should be
* subdivided or left as-is. This is used once for each call to
* AdaptivelySample1Facet (which is recursive and will call itself
* resulting in additional edges to be checked) or three times for
* each call to AdaptivelySample2Facet (also recursive).
*/
virtual void SetSubdivisionAlgorithm(vtkEdgeSubdivisionCriterion*);
virtual vtkEdgeSubdivisionCriterion* GetSubdivisionAlgorithm();
///@}
virtual const vtkEdgeSubdivisionCriterion* GetSubdivisionAlgorithm() const;
///@{
/**
* Get/Set the number of parameter-space coordinates associated with each input and output point.
* The default is \a k for \a k -facets. You may
* specify a different dimension, \a d, for each type of \a k -facet to be processed.
* For example, \p SetEmbeddingDimension( \p 2, \p 3 ) would associate \a r, \a s, and
* \a t coordinates with each input and output point generated by \p AdaptivelySample2Facet
* but does not say anything about input or output points generated by
* \p AdaptivelySample1Facet.
* Call \p SetEmbeddingDimension( \p -1, \a d ) to specify the same dimension for
* all possible \a k values.
* \a d may not exceed 8, as that would be plain silly.
*/
virtual void SetEmbeddingDimension(int k, int d);
int GetEmbeddingDimension(int k) const;
///@}
///@{
/**
* Get/Set the number of field value coordinates associated with each input and output point.
* The default is 0; no field values are interpolated.
* You may specify a different size, \a s, for each type of \a k -facet to be processed.
* For example, \p SetFieldSize( \p 2, \p 3 ) would associate 3 field value coordinates
* with each input and output point of an \p AdaptivelySample2Facet call,
* but does not say anything about input or output points of \p AdaptivelySample1Facet.
* Call \p SetFieldSize( \p -1, \a s ) to specify the same dimension for all possible \a k values.
* \a s may not exceed vtkStreamingTessellator::MaxFieldSize.
* This is a compile-time constant that defaults to 18, which is large enough for
* a scalar, vector, tensor, normal, and texture coordinate to be included at each point.
* Normally, you will not call \a SetFieldSize() directly; instead, subclasses of
* vtkEdgeSubdivisionCriterion, such as vtkShoeMeshSubdivisionAlgorithm, will call it
* for you.
* In any event, setting \a FieldSize to a non-zero value means you must pass field
* values to the \p AdaptivelySamplekFacet routines; For example,
* @verbatim
* vtkStreamingTessellator* t = vtkStreamingTessellator::New();
* t->SetFieldSize( 1, 3 );
* t->SetEmbeddingDimension( 1, 1 ); // not really required, this is the default
* double p0[3+1+3] = { x0, y0, z0, r0, fx0, fy0, fz0 };
* double p1[3+1+3] = { x1, y1, z1, r1, fx1, fy1, fz1 };
* t->AdaptivelySample1Facet( p0, p1 );
* @endverbatim
* This would adaptively sample an curve (1-facet) with geometry and
* a vector field at every output point on the curve.
*/
virtual void SetFieldSize(int k, int s);
int GetFieldSize(int k) const;
///@}
///@{
/**
* Get/Set the maximum number of subdivisions that may occur.
*/
virtual void SetMaximumNumberOfSubdivisions(int num_subdiv_in);
int GetMaximumNumberOfSubdivisions();
///@}
///@{
/**
* This will adaptively subdivide the tetrahedron (3-facet),
* triangle (2-facet), or edge (1-facet) until the subdivision
* algorithm returns false for every edge or the maximum recursion
* depth is reached.
* Use \p SetMaximumNumberOfSubdivisions to change the maximum
* recursion depth.
* The AdaptivelySample0Facet method is provided as a convenience.
* Obviously, there is no way to adaptively subdivide a vertex.
* Instead the input vertex is passed unchanged to the output
* via a call to the registered VertexProcessorFunction callback.
* .SECTION Warning
* This assumes that you have called SetSubdivisionAlgorithm(),
* SetEdgeCallback(), SetTriangleCallback(), and SetTetrahedronCallback()
* with valid values!
*/
void AdaptivelySample3FacetLinear(double* v0, double* v1, double* v2, double* v3) const;
void AdaptivelySample2FacetLinear(double* v0, double* v1, double* v2) const;
void AdaptivelySample1FacetLinear(double* v0, double* v1) const;
void AdaptivelySample3Facet(double* v0, double* v1, double* v2, double* v3) const;
void AdaptivelySample2Facet(double* v0, double* v1, double* v2) const;
void AdaptivelySample1Facet(double* v0, double* v1) const;
void AdaptivelySample0Facet(double* v0) const;
///@}
///@{
/**
* Reset/access the histogram of subdivision cases encountered.
* The histogram may be used to examine coverage during testing as well as characterizing the
* tessellation algorithm's performance.
* You should call ResetCounts() once, at the beginning of a stream of tetrahedra.
* It must be called before AdaptivelySample3Facet() to prevent uninitialized memory reads.
* These functions have no effect (and return 0) when PARAVIEW_DEBUG_TESSELLATOR has not been
defined.
* By default, PARAVIEW_DEBUG_TESSELLATOR is not defined, and your code will be fast and
efficient. Really!
*/
void ResetCounts()
{
#ifdef PARAVIEW_DEBUG_TESSELLATOR
for (int i = 0; i < 11; ++i)
{
this->CaseCounts[i] = 0;
for (int j = 0; j < 51; ++j)
{
this->SubcaseCounts[i][j] = 0;
}
}
#endif // PARAVIEW_DEBUG_TESSELLATOR
}
vtkIdType GetCaseCount(int c)
{
#ifdef PARAVIEW_DEBUG_TESSELLATOR
return this->CaseCounts[c];
#else
(void)c;
return 0;
#endif // PARAVIEW_DEBUG_TESSELLATOR
}
vtkIdType GetSubcaseCount(int casenum, int sub)
{
#ifdef PARAVIEW_DEBUG_TESSELLATOR
return this->SubcaseCounts[casenum][sub];
#else
(void)casenum;
(void)sub;
return 0;
#endif // PARAVIEW_DEBUG_TESSELLATOR
}
///@}
protected:
static int EdgeCodesToCaseCodesPlusPermutation[64][2];
static vtkIdType PermutationsFromIndex[24][14];
static vtkIdType TetrahedralDecompositions[];
void* PrivateData;
const void* ConstPrivateData;
vtkEdgeSubdivisionCriterion* Algorithm;
VertexProcessorFunction Callback0;
EdgeProcessorFunction Callback1;
TriangleProcessorFunction Callback2;
TetrahedronProcessorFunction Callback3;
#ifdef PARAVIEW_DEBUG_TESSELLATOR
mutable vtkIdType CaseCounts[11];
mutable vtkIdType SubcaseCounts[11][51];
#endif // PARAVIEW_DEBUG_TESSELLATOR
/**
* PointDimension is the length of each \p double* array associated with
* each point passed to a subdivision algorithm:
* PointDimension[i] = 3 + EmbeddingDimension[i] + FieldSize[i]
* We store this instead of FieldSize for speed.
* Only entries 1 through 3 are used; you can't subdivide 0-facets (points).
* Well, maybe you can, but I can't!
*/
int PointDimension[4];
/**
* The parametric dimension of each point passed to the subdivision algorithm.
* Only entries 1 through 3 are used; you can't subdivide 0-facets (points).
* Well, maybe you can, but I can't!
*/
int EmbeddingDimension[4];
/**
* The number of subdivisions allowed.
*/
int MaximumNumberOfSubdivisions;
vtkStreamingTessellator();
~vtkStreamingTessellator() override;
void AdaptivelySample3Facet(double* v0, double* v1, double* v2, double* v3, int maxDepth) const;
void AdaptivelySample2Facet(double* v0, double* v1, double* v2, int maxDepth, int move = 7) const;
void AdaptivelySample1Facet(double* v0, double* v1, int maxDepth) const;
int BestTets(int*, double**, int, int) const;
private:
vtkStreamingTessellator(const vtkStreamingTessellator&) = delete;
void operator=(const vtkStreamingTessellator&) = delete;
};
inline void vtkStreamingTessellator::AdaptivelySample3Facet(
double* v0, double* v1, double* v2, double* v3) const
{
this->AdaptivelySample3Facet(v0, v1, v2, v3, this->MaximumNumberOfSubdivisions);
}
inline void vtkStreamingTessellator::AdaptivelySample2Facet(
double* v0, double* v1, double* v2) const
{
this->AdaptivelySample2Facet(v0, v1, v2, this->MaximumNumberOfSubdivisions);
}
inline void vtkStreamingTessellator::AdaptivelySample1Facet(double* v0, double* v1) const
{
this->AdaptivelySample1Facet(v0, v1, this->MaximumNumberOfSubdivisions);
}
inline int vtkStreamingTessellator::GetEmbeddingDimension(int k) const
{
if (k <= 0 || k >= 4)
return -1;
return this->EmbeddingDimension[k];
}
inline int vtkStreamingTessellator::GetFieldSize(int k) const
{
if (k <= 0 || k >= 4)
return -1;
return this->PointDimension[k] - this->EmbeddingDimension[k] - 3;
}
inline int vtkStreamingTessellator::GetMaximumNumberOfSubdivisions()
{
return this->MaximumNumberOfSubdivisions;
}
VTK_ABI_NAMESPACE_END
#endif // vtkStreamingTessellator_h