I have added my extension here, which is quite convenient for working with TNB
. You have a callback for drawing the TNB frame.
#pragma once
#include "vtkOrientedGlyphContourRepresentation.h"
#include "vtkInteractionWidgetsModule.h" // For export macro
class vtkProperty;
class vtkActor;
class vtkGlyph3DMapper;
class vtkPolyData;
class vtkProperty;
class vtkCallbackCommand;
class vtkTNB;
class vtkCellLocator;
class vtkArrowSource;
class vtkTransform;
struct TNBPipeLineData;
class VTKINTERACTIONWIDGETS_EXPORT vtkOrientedGlyphContourRepresentationEx
: public vtkOrientedGlyphContourRepresentation
{
public:
static vtkOrientedGlyphContourRepresentationEx* New();
vtkTypeMacro(vtkOrientedGlyphContourRepresentationEx, vtkOrientedGlyphContourRepresentation)
void PrintSelf(std::ostream& os, vtkIndent indent) override;
vtkSetMacro(ArrowScale, double);
vtkGetMacro(ArrowScale, double);
vtkSetMacro(ReductionFactor, double);
vtkGetMacro(ReductionFactor, double);
/**
* Get the intermediate points in this contour as a vtkPolyData
*/
void GetIntermediatePolyData(vtkPolyData* poly);
void BuildRepresentation() override;
///@{
/**
*/
vtkGetObjectMacro(NormalsProperty, vtkProperty);
vtkGetObjectMacro(BinormalsProperty, vtkProperty);
vtkGetObjectMacro(TangentsProperty, vtkProperty);
///@}
///@{
/**
* Methods to make this class behave as a vtkProp.
*/
void GetActors(vtkPropCollection*) override;
void ReleaseGraphicsResources(vtkWindow*) override;
int RenderOverlay(vtkViewport* viewport) override;
int RenderOpaqueGeometry(vtkViewport* viewport) override;
int RenderTranslucentPolygonalGeometry(vtkViewport* viewport) override;
vtkTypeBool HasTranslucentPolygonalGeometry() override;
///@}
void TNBCallback(vtkObject* caller, int ev);
void TNBCallback(vtkObject* caller, const char* evString);
void SetPolyData(vtkPolyData* polyData);
void GetTransformAtIndex(vtkIdType index, vtkTransform* transform);
class VTKINTERACTIONWIDGETS_EXPORT vtkInternals;
protected:
double ArrowScale;
double ReductionFactor;
vtkActor* NormalsActor;
vtkActor* TangentsActor;
vtkActor* BinormalsActor;
vtkPolyData* Normals;
vtkPolyData* Tangents;
vtkPolyData* Binormals;
vtkGlyph3DMapper* NormalsMapper;
vtkGlyph3DMapper* TangentsMapper;
vtkGlyph3DMapper* BinormalsMapper;
vtkProperty* NormalsProperty;
vtkProperty* BinormalsProperty;
vtkProperty* TangentsProperty;
vtkTNB* Frame;
vtkCellLocator* CellLocator;
vtkPolyData* PolyData;
vtkArrowSource* ArrowSource;
vtkInternals* Internals;
friend class vtkInternals;
private:
vtkOrientedGlyphContourRepresentationEx();
~vtkOrientedGlyphContourRepresentationEx();
};
and the code here
#include <vtkCallbackCommand.h>
#include <vtkCellArray.h>
#include <vtkOrientedGlyphContourRepresentationEx.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
// Option for interpolation, coarse/fine, show/hide, orientation for an index
// Test TNB Observer
#include <vtkArrowSource.h>
#include <vtkCellData.h>
#include <vtkCellLocator.h>
#include <vtkContourWidget.h>
#include <vtkGlyph3DMapper.h>
#include <vtkMaskPoints.h>
#include <vtkMatrix4x4.h>
#include <vtkPointData.h>
#include <vtkPointPlacer.h>
#include <vtkPolyDataCollection.h>
#include <vtkPolygonalSurfaceContourLineInterpolator.h>
#include <vtkPolygonalSurfacePointPlacer.h> // Is this used?
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSurfacePathInterpolator.h> // Using Dijkstra
#include <vtkTNB.h>
#include <vtkTransform.h>
#include <vtkVector.h>
vtkStandardNewMacro(vtkOrientedGlyphContourRepresentationEx);
class vtkOrientedGlyphContourRepresentationEx::vtkInternals
{
public:
vtkInternals(vtkOrientedGlyphContourRepresentationEx* self)
: Self(self)
{
}
vtkOrientedGlyphContourRepresentationEx* Self;
void Execute(vtkObject* caller, unsigned long vtkNotUsed(evId))
{
vtkContourWidget* cw = vtkContourWidget::SafeDownCast(caller);
if (cw)
{
auto rep = vtkOrientedGlyphContourRepresentationEx::SafeDownCast(cw->GetRepresentation());
if (rep)
{
// Cell normals are required!!!
vtkPolyData* surfacePolyData = vtkPolyData::SafeDownCast(Self->PolyData);
if (!surfacePolyData)
{
std::cerr << "No polydata associated with representation\n";
return;
}
if (!surfacePolyData->GetCellData()->GetNormals())
{
std::cerr << "No cell normals defined for polydata\n";
return;
}
vtkNew<vtkPolyData> pd;
rep->GetIntermediatePolyData(pd);
if (pd->GetNumberOfPoints() < 2)
{
return;
}
// Reset orientation
Self->Frame->SetViewUp(0.0);
Self->Frame->SetInputData(pd);
// Input either masked or not
vtkPolyDataAlgorithm* pipeLineInput;
vtkNew<vtkMaskPoints> maskPoints;
maskPoints->SetInputConnection(Self->Frame->GetOutputPort());
int everyNth = Self->ReductionFactor == 0.0 ? 1.0 : int(1.0 / Self->ReductionFactor);
maskPoints->SetOnRatio(everyNth);
maskPoints->Update();
// Masked input - every nth point
pipeLineInput = maskPoints.GetPointer();
// Tangent, normal and binormal for the first point.
double *pNormal, *pTangent, *pBiNormal;
pNormal = pipeLineInput->GetOutput()->GetPointData()->GetArray("FSNormals")->GetTuple(0);
pBiNormal =
pipeLineInput->GetOutput()->GetPointData()->GetArray("FSBinormals")->GetTuple(0);
pTangent = pipeLineInput->GetOutput()->GetPointData()->GetArray("FSTangents")->GetTuple(0);
double cellNormal[3];
// Locate a normal on the surface near the first point
vtkIdType cellId;
int subId;
double closestPoint[3];
double distance = 0.0;
Self->CellLocator->FindClosestPoint(pd->GetPoint(0), closestPoint, cellId, subId, distance);
surfacePolyData->GetCellData()->GetNormals()->GetTuple(cellId, cellNormal);
// Project cellNormal vector into the NxB plane and adjust N to align with cellNormal
double UdotT = vtkMath::Dot(cellNormal, pTangent);
for (int i = 0; i < 3; i++)
{
cellNormal[i] = cellNormal[i] - UdotT * pTangent[i];
}
vtkVector3d cellNormalMinusT = vtkVector3d(cellNormal);
cellNormalMinusT.Normalize();
double UdotN = vtkMath::Dot(cellNormalMinusT.GetData(), pNormal);
double UdotB = vtkMath::Dot(cellNormalMinusT.GetData(), pBiNormal);
// Angle in radians used for adjustment
double angleAdjust = std::atan2(UdotB, UdotN);
Self->Frame->SetViewUp(angleAdjust);
pipeLineInput->SetInputConnection(Self->Frame->GetOutputPort());
pipeLineInput->Update();
// Normals
pipeLineInput->GetOutput()->GetPointData()->SetActiveVectors("FSNormals");
Self->Normals->DeepCopy(pipeLineInput->GetOutput());
Self->NormalsMapper->SetInputData(Self->Normals);
Self->NormalsMapper->SetScaleFactor(Self->ArrowScale);
Self->NormalsMapper->SetSourceConnection(Self->ArrowSource->GetOutputPort());
Self->NormalsMapper->Update();
// Tangents
pipeLineInput->GetOutput()->GetPointData()->SetActiveVectors("FSTangents");
Self->Tangents->DeepCopy(pipeLineInput->GetOutput());
Self->TangentsMapper->SetInputData(Self->Tangents);
Self->TangentsMapper->SetScaleFactor(Self->ArrowScale);
Self->TangentsMapper->SetSourceConnection(Self->ArrowSource->GetOutputPort());
Self->TangentsMapper->Update();
// Bi-normals
pipeLineInput->GetOutput()->GetPointData()->SetActiveVectors("FSBinormals");
Self->Binormals->DeepCopy(pipeLineInput->GetOutput());
Self->BinormalsMapper->SetInputData(pipeLineInput->GetOutput());
Self->BinormalsMapper->SetScaleFactor(Self->ArrowScale);
Self->BinormalsMapper->SetSourceConnection(Self->ArrowSource->GetOutputPort());
Self->BinormalsMapper->Update();
}
}
}
private:
double ReductionFactor;
};
void vtkOrientedGlyphContourRepresentationEx::SetPolyData(vtkPolyData* polydata)
{
if (this->PolyData == polydata)
{
return;
}
if (this->PolyData)
{
this->PolyData->UnRegister(this);
}
this->PolyData = polydata;
if (this->PolyData)
{
this->PolyData->Register(this);
this->CellLocator->SetDataSet(this->PolyData);
this->CellLocator->BuildLocator();
}
}
//------------------------------------------------------------------------------
vtkOrientedGlyphContourRepresentationEx::vtkOrientedGlyphContourRepresentationEx()
{
this->ArrowScale = 1.0;
this->ReductionFactor = 0.0;
this->Normals = vtkPolyData::New();
this->NormalsMapper = vtkGlyph3DMapper::New();
this->NormalsActor = vtkActor::New();
this->NormalsActor->SetMapper(this->NormalsMapper);
this->NormalsProperty = vtkProperty::New();
this->NormalsProperty->SetColor(1.0, 0.0, 0.0);
this->NormalsProperty->SetRepresentationToSurface();
this->NormalsProperty->SetAmbient(1.0);
this->NormalsProperty->SetDiffuse(0.0);
this->NormalsProperty->SetSpecular(0.0);
this->NormalsProperty->SetLineWidth(1.0);
this->NormalsActor->SetProperty(this->NormalsProperty);
this->Tangents = vtkPolyData::New();
this->TangentsMapper = vtkGlyph3DMapper::New();
this->TangentsActor = vtkActor::New();
this->TangentsActor->SetMapper(this->TangentsMapper);
this->TangentsProperty = vtkProperty::New();
this->TangentsProperty->SetColor(0.0, 1.0, 0.0);
this->TangentsProperty->SetRepresentationToSurface();
this->TangentsProperty->SetAmbient(1.0);
this->TangentsProperty->SetDiffuse(0.0);
this->TangentsProperty->SetSpecular(0.0);
this->TangentsProperty->SetLineWidth(1.0);
this->TangentsActor->SetProperty(this->TangentsProperty);
this->Binormals = vtkPolyData::New();
this->BinormalsMapper = vtkGlyph3DMapper::New();
this->BinormalsActor = vtkActor::New();
this->BinormalsActor->SetMapper(this->BinormalsMapper);
this->BinormalsProperty = vtkProperty::New();
this->BinormalsProperty->SetColor(0.0, 0.0, 1.0);
this->BinormalsProperty->SetRepresentationToSurface();
this->BinormalsProperty->SetAmbient(1.0);
this->BinormalsProperty->SetDiffuse(0.0);
this->BinormalsProperty->SetSpecular(0.0);
this->BinormalsProperty->SetLineWidth(1.0);
this->BinormalsActor->SetProperty(this->BinormalsProperty);
this->Frame = vtkTNB::New();
this->Frame->ConsistentNormalsOn();
this->CellLocator = vtkCellLocator::New();
this->PolyData = nullptr;
this->ArrowSource = vtkArrowSource::New();
this->ArrowSource->SetTipResolution(16);
this->ArrowSource->SetTipLength(.3);
this->ArrowSource->SetTipRadius(.1);
this->Internals = new vtkInternals(this);
}
//------------------------------------------------------------------------------
vtkOrientedGlyphContourRepresentationEx::~vtkOrientedGlyphContourRepresentationEx()
{
this->Normals->Delete();
this->NormalsMapper->Delete();
this->NormalsActor->Delete();
this->Tangents->Delete();
this->TangentsMapper->Delete();
this->TangentsActor->Delete();
this->TangentsProperty->Delete();
this->Binormals->Delete();
this->BinormalsMapper->Delete();
this->BinormalsActor->Delete();
this->BinormalsProperty->Delete();
this->ArrowSource->Delete();
this->Frame->Delete();
this->CellLocator->Delete();
this->NormalsProperty->Delete();
this->SetPolyData(nullptr);
if (this->Internals)
{
delete this->Internals;
this->Internals = nullptr;
}
}
//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentationEx::PrintSelf(std::ostream& os, vtkIndent indent)
{
Superclass::PrintSelf(os, indent);
if (this->NormalsProperty)
{
os << indent << "Normals Property: " << this->NormalsProperty << "\n";
}
else
{
os << indent << "Normals Property: (none)\n";
}
if (this->BinormalsProperty)
{
os << indent << "Binormals Property: " << this->BinormalsProperty << "\n";
}
else
{
os << indent << "Binormals Property: (none)\n";
}
if (this->TangentsProperty)
{
os << indent << "Tangents Property: " << this->TangentsProperty << "\n";
}
else
{
os << indent << "Tangents Property: (none)\n";
}
}
// vtkCommand* vtkOrientedGlyphContourRepresentationEx::GetTNBObserver()
//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentationEx::GetIntermediatePolyData(vtkPolyData* poly)
{
poly->Initialize();
int count = this->GetNumberOfNodes();
if (count == 0)
{
return;
}
vtkNew<vtkPoints> points;
vtkNew<vtkCellArray> lines;
int i, j;
vtkIdType index = 0;
count = 0;
for (i = 0; i < this->GetNumberOfNodes(); i++)
{
count += this->GetNumberOfIntermediatePoints(i);
}
points->SetNumberOfPoints(count);
vtkIdType numLines;
if (this->ClosedLoop && count > 0)
{
numLines = count + 1;
}
else
{
numLines = count;
}
if (numLines > 0)
{
vtkIdType* lineIndices = new vtkIdType[numLines];
double pos[3];
for (i = 0; i < this->GetNumberOfNodes(); i++)
{
int numIntermediatePoints = this->GetNumberOfIntermediatePoints(i);
for (j = 0; j < numIntermediatePoints; j++)
{
Superclass::GetIntermediatePointWorldPosition(i, j, pos);
points->InsertPoint(index, pos);
if (index < numLines)
{
lineIndices[index] = index;
}
index++;
}
}
if (Superclass::ClosedLoop)
{
if (index < numLines)
lineIndices[index] = 0;
}
lines->InsertNextCell(numLines, lineIndices);
delete[] lineIndices;
}
poly->SetPoints(points);
poly->SetLines(lines);
}
void vtkOrientedGlyphContourRepresentationEx::TNBCallback(vtkObject* caller, int ev)
{
this->Internals->Execute(caller, ev);
}
void vtkOrientedGlyphContourRepresentationEx::TNBCallback(vtkObject* caller, const char* evString)
{
this->TNBCallback(caller, vtkCommand::GetEventIdFromString(evString));
}
void vtkOrientedGlyphContourRepresentationEx::BuildRepresentation()
{
Superclass::BuildRepresentation();
}
int vtkOrientedGlyphContourRepresentationEx::RenderTranslucentPolygonalGeometry(
vtkViewport* viewport)
{
int count = Superclass::RenderTranslucentPolygonalGeometry(viewport);
count += this->NormalsActor->RenderTranslucentPolygonalGeometry(viewport);
count += this->BinormalsActor->RenderTranslucentPolygonalGeometry(viewport);
count += this->TangentsActor->RenderTranslucentPolygonalGeometry(viewport);
return count;
}
vtkTypeBool vtkOrientedGlyphContourRepresentationEx::HasTranslucentPolygonalGeometry()
{
int result = Superclass::HasTranslucentPolygonalGeometry();
if (this->NormalsActor->GetVisibility())
{
result |= this->NormalsActor->HasTranslucentPolygonalGeometry();
}
if (this->BinormalsActor->GetVisibility())
{
result |= this->BinormalsActor->HasTranslucentPolygonalGeometry();
}
if (this->TangentsActor->GetVisibility())
{
result |= this->TangentsActor->HasTranslucentPolygonalGeometry();
}
return result;
}
void vtkOrientedGlyphContourRepresentationEx::GetActors(vtkPropCollection* pc)
{
Superclass::GetActors(pc);
this->TangentsActor->GetActors(pc);
this->NormalsActor->GetActors(pc);
this->BinormalsActor->GetActors(pc);
}
int vtkOrientedGlyphContourRepresentationEx::RenderOverlay(vtkViewport* viewport)
{
int count = Superclass::RenderOverlay(viewport);
count += this->NormalsActor->RenderOverlay(viewport);
count += this->BinormalsActor->RenderOverlay(viewport);
count += this->TangentsActor->RenderOverlay(viewport);
return count;
}
int vtkOrientedGlyphContourRepresentationEx::RenderOpaqueGeometry(vtkViewport* viewport)
{
int count = Superclass::RenderOpaqueGeometry(viewport);
count += this->NormalsActor->RenderOpaqueGeometry(viewport);
count += this->BinormalsActor->RenderOpaqueGeometry(viewport);
count += this->TangentsActor->RenderOpaqueGeometry(viewport);
return count;
}
void vtkOrientedGlyphContourRepresentationEx::ReleaseGraphicsResources(vtkWindow* win)
{
Superclass::ReleaseGraphicsResources(win);
this->NormalsActor->ReleaseGraphicsResources(win);
this->BinormalsActor->ReleaseGraphicsResources(win);
this->TangentsActor->ReleaseGraphicsResources(win);
}
void vtkOrientedGlyphContourRepresentationEx::GetTransformAtIndex(
vtkIdType index, vtkTransform* transform)
{
vtkIdType nTransforms = this->Binormals->GetNumberOfPoints();
if (index < nTransforms)
{
double* pBinormal = this->Binormals->GetPointData()->GetArray("FSBinormals")->GetTuple(index);
double* pNormal = this->Binormals->GetPointData()->GetArray("FSNormals")->GetTuple(index);
double* pTangent = this->Binormals->GetPointData()->GetArray("FSTangents")->GetTuple(index);
double* pPosition = this->Binormals->GetPoint(index);
vtkNew<vtkMatrix4x4> matrix;
matrix->Identity();
matrix->SetElement(0, 0, pBinormal[0]);
matrix->SetElement(0, 1, pTangent[0]);
matrix->SetElement(0, 2, pNormal[0]);
matrix->SetElement(1, 0, pBinormal[1]);
matrix->SetElement(1, 1, pTangent[1]);
matrix->SetElement(1, 2, pNormal[1]);
matrix->SetElement(2, 0, pBinormal[2]);
matrix->SetElement(2, 1, pTangent[2]);
matrix->SetElement(2, 2, pNormal[2]);
matrix->SetElement(0, 3, pPosition[0]);
matrix->SetElement(1, 3, pPosition[1]);
matrix->SetElement(2, 3, pPosition[2]);
transform->SetMatrix(matrix);
}
}