// 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