VTK 三角剖分 Delaunay2D (三)—— 自定義Filter類

Filter類定義了RequestData()函數,當Filter調用Update()後,響應RequestData()函數。

VTK管線機制

VTK中通過管線機制來實現組合各種算法處理數據。每一種算法是一個Filter,多個Filter連接一起形成了VTK管線。每個Filter可以分爲兩個組成部分:一個是算法部分,繼承自vtkAlgorithm,主要負責處理輸入的數據和信息;另一個是執行對象,繼承自vtkExecutive,負責通知算法對象何時運行以及傳遞需要處理的數據和信息。

Filter類繼承自vtkAlgorithm及其 子類,實例化時,其內部會生成一個默認的vtkExecutive對象,用於管理執行管線。
  
數據和信息通過端口在Filter中傳遞,根據數據流的方向,分爲輸入端口和輸出端口。Filter之間通過端口(Port)建立連接。

VTK中定義了一個vtkInformation類,用於存儲和傳遞管線執行過程中的信息、請求和數據,實現執行管線的連接和控制。
vtkInformation是實現VTK執行管線的一個非常重要的類。它是一個Map容器,採用Key-Value的映射方法,通過索引(Key)的類型來決定其對應的數據,用於存儲管線中的各種信息和數據。

vtkInformation類中 索引Key的類型爲vtkInformationKey的子類,VTK定義了大量的索引類型,這些類都繼承自vtkInformationKey

基本步驟

定義管線接口
vtkTestAlgorithmFilter::vtkTestAlgorithmFilter()
{
  //兩個輸入,一個輸出
  this->SetNumberOfInputPorts( 2 );
  this->SetNumberOfOutputPorts( 1 );
}

vtkAlgorithm::FillInputPortInformation()設置輸入端口信息;每個輸出端口對應一個輸出端口信息對象,使用vtkAlgorithm::FillOutputPortInformation()設置輸出端口信息。由於直接繼承自vtkPolyDataAlgorithm類,只需重寫FillOutputPortInformation

int vtkTestAlgorithmFilter::FillInputPortInformation(
int port, vtkInformation* info)
{
	if ( port == 0)
	{
		info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
	}
	else if (port == 1)
	{
		info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
		//info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
	}
	return 1;
}
定義用戶接口

Filter需要定義多個用戶接口來設置參數,以調節Filter執行效果。對於每個Filter,需要覆蓋基類的PrintSelf()函數。

實現管線請求

參見vtkPolyDataAlgorithm中的ProcessRequest(),如果直接繼承自vtkPolyDataAlgorithm,不用實現。

實現算法核心部分

REQUEST_DATA()是執行Filter的主要函數。

int vtkTestAlgorithmFilter::RequestData(
                                  vtkInformation* vtkNotUsed(request),
    vtkInformationVector **inputVector,
    vtkInformationVector* outputVector )

示例

vtkTestAlgorithmFilter.h

#ifndef __vtkTestAlgorithmFilter_h
#define __vtkTestAlgorithmFilter_h

#include <vtkPolyDataAlgorithm.h>
class vtkDataSet;
class vtkPolyData;

class vtkTestAlgorithmFilter : public vtkPolyDataAlgorithm
{
public:
  static vtkTestAlgorithmFilter *New();
  vtkTypeMacro(vtkTestAlgorithmFilter, vtkPolyDataAlgorithm);
  void PrintSelf(ostream& os, vtkIndent indent) override;

protected:
  vtkTestAlgorithmFilter();
  ~vtkTestAlgorithmFilter();

  // Description:
  // This is called by the superclass.
  // This is the method you should override.
  virtual int RequestData(
    vtkInformation* request,
    vtkInformationVector** inputVector,
    vtkInformationVector* outputVector );
  
  virtual int FillInputPortInformation( int port, vtkInformation* info ) override;

};

#endif

vtkTestAlgorithmFilter.cxx

#include "vtkTestAlgorithmFilter.h"

#include <vtkInformation.h>
#include <vtkInformationVector.h>

#include <vtkFeatureEdges.h>
#include <vtkStripper.h>
#include <vtkCleanPolyData.h>
#include <vtkAppendPolyData.h>

vtkStandardNewMacro(vtkTestAlgorithmFilter);

//----------------------------------------------------------------------------
vtkTestAlgorithmFilter::vtkTestAlgorithmFilter()
{
  //
  this->SetNumberOfInputPorts( 2 );
  this->SetNumberOfOutputPorts( 1 );
}

//----------------------------------------------------------------------------
vtkTestAlgorithmFilter::~vtkTestAlgorithmFilter()
{
}

//----------------------------------------------------------------------------
void vtkTestAlgorithmFilter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}

//----------------------------------------------------------------------------
int vtkTestAlgorithmFilter::FillInputPortInformation(
int port, vtkInformation* info)
{
	if ( port == 0)
	{
		info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
	}
	else if (port == 1)
	{
		info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
		//info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
	}
	return 1;
}


//----------------------------------------------------------------------------
// This is the superclasses style of Execute method.  Convert it into
// an imaging style Execute method.
int vtkTestAlgorithmFilter::RequestData(
                                  vtkInformation* vtkNotUsed(request),
    vtkInformationVector **inputVector,
    vtkInformationVector* outputVector )
{
//Later on RequestData (RD) happens.
//During RD each filter examines any inputs it has, then fills in that empty data object with real data.

  vtkInformation *inInfoOne = inputVector[0]->GetInformationObject(0);
  vtkInformation *inInfoTwo = inputVector[1]->GetInformationObject(0);
  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  vtkPolyData *sourceOne = vtkPolyData::SafeDownCast(inInfoOne->Get(vtkDataObject::DATA_OBJECT()));
  vtkPolyData *sourceTwo = vtkPolyData::SafeDownCast(inInfoTwo->Get(vtkDataObject::DATA_OBJECT()));;
  vtkPolyData *output = vtkPolyData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT()));
  
  //獲取三角網邊界點集
  vtkSmartPointer<vtkFeatureEdges> fEdgesOne = vtkSmartPointer<vtkFeatureEdges>::New();
  fEdgesOne->SetInputData(sourceOne);
  fEdgesOne->BoundaryEdgesOn();
  fEdgesOne->FeatureEdgesOff();
  fEdgesOne->ManifoldEdgesOff();
  fEdgesOne->NonManifoldEdgesOff();
  fEdgesOne->Update();

  //使點集有序
  vtkSmartPointer<vtkStripper> stripperOne = vtkSmartPointer<vtkStripper>::New();
  stripperOne->SetInputConnection(fEdgesOne->GetOutputPort());

  vtkSmartPointer<vtkCleanPolyData> cleanPolyDataOne =
	  vtkSmartPointer<vtkCleanPolyData>::New();
  cleanPolyDataOne->SetInputConnection(stripperOne->GetOutputPort());
  cleanPolyDataOne->Update();

  vtkPoints *pt = nullptr;
  double* p;
  unsigned int num1, num2;

  pt = cleanPolyDataOne->GetOutput()->GetPoints();
  num1 = pt->GetNumberOfPoints();

  //拷貝到邊界點集中
  vtkSmartPointer<vtkPoints> boundary1 = vtkSmartPointer<vtkPoints>::New();
 
  for (unsigned int i = 0; i < num1; ++i) {
	  p = pt->GetPoint(i);
	  boundary1->InsertNextPoint(p);
  }

  vtkSmartPointer<vtkFeatureEdges> fEdgesTwo = vtkSmartPointer<vtkFeatureEdges>::New();
  fEdgesTwo->SetInputData(sourceTwo);
  fEdgesTwo->BoundaryEdgesOn();
  fEdgesTwo->FeatureEdgesOff();
  fEdgesTwo->ManifoldEdgesOff();
  fEdgesTwo->NonManifoldEdgesOff();
  fEdgesTwo->Update();

  vtkSmartPointer<vtkStripper> stripperTwo = vtkSmartPointer<vtkStripper>::New();
  stripperTwo->SetInputConnection(fEdgesTwo->GetOutputPort());

  vtkSmartPointer<vtkCleanPolyData> cleanPolyDataTwo = vtkSmartPointer<vtkCleanPolyData>::New();
  cleanPolyDataTwo->SetInputConnection(stripperTwo->GetOutputPort());
  cleanPolyDataTwo->Update();
  pt = cleanPolyDataTwo->GetOutput()->GetPoints();
  num2 = pt->GetNumberOfPoints();
  //將第二個邊界點集拷貝到邊界點集中
  vtkSmartPointer<vtkPoints> boundary2 = vtkSmartPointer<vtkPoints>::New();
  for (unsigned int i = 0; i < num1; ++i) {
	  p = pt->GetPoint(i);
	  boundary2->InsertNextPoint(p);

  }

  /*
  * 由上下兩個邊界環生成三角網
  * 原則:按照最短對角線原則
  * 方法:上下兩個邊界環,依次組成四邊形,比較對角線的大小,生成三角形
  */
  vtkPoints *boundary = vtkPoints::New();

  for (unsigned int i = 0; i < num1; ++i) {
	  p = boundary1->GetPoint(i);
	  boundary->InsertNextPoint(p);
  }

  for (unsigned int i = 0; i < num2; ++i) {
	  p = boundary2->GetPoint(i);
	  boundary->InsertNextPoint(p);
  }

  vtkIdType pts[3];               //保存三角形三個頂點的索引
  vtkCellArray* triangles = vtkCellArray::New();
  vtkPolyData*  Mesh = vtkPolyData::New();
  triangles->Allocate(triangles->EstimateSize(2 * (num1 + num2), 3));
  Mesh->SetPoints(boundary);
  Mesh->SetPolys(triangles);

  double d0 = 0.0, d1 = 0.0;         //保存四邊形兩條對角線的長度
  double p1[3], p2[3], p3[3], p4[3]; //四邊形的四個頂點
  unsigned int n1 = 0, n2 = 0;        //上下邊界環當前遍歷的位置
  unsigned int start1 = 0, start2 = 0;//上下邊界環遍歷的起始位置

  start1 = 0;// 設置第一條邊界環遍歷的起始位置 start1=0
  boundary1->GetPoint(0, p1);
  //尋找第二條邊的起始位置 start2 (距離最近的點)
  d1 = VTK_DOUBLE_MAX;
  for (unsigned int i = 0; i < num2; ++i) {
	  boundary2->GetPoint(i, p2);
	  d0 = vtkMath::Distance2BetweenPoints(p1, p2);
	  if (d0 < d1) {
		  d1 = d0;
		  start2 = i;
	  }
  }

  n2 = start2;
  bool flag = false;
  do {
	  // 組成四邊形的四個點
	  // p1,p2 一條邊界環的點
	  // p3,p4 另一條邊界環的點

	  boundary1->GetPoint(n1, p1);
	  boundary1->GetPoint((n1 + 1) % num1, p2);

	  boundary2->GetPoint(n2, p3);
	  boundary2->GetPoint((n2 + 1) % num2, p4);

	  d0 = vtkMath::Distance2BetweenPoints(p1, p4);
	  d1 = vtkMath::Distance2BetweenPoints(p2, p3);

	  pts[0] = n1%num1;
	  pts[1] = n2%num2 + num1;

	  if (d0 <= d1) {
		  pts[2] = (n2 + 1) % num2 + num1;
		  n2 = (n2 + 1) % num2;

	  }
	  else {
		  pts[2] = (n1 + 1) % num1;
		  n1 = (n1 + 1) % num1;
	  }
	  triangles->InsertNextCell(3, pts);

	  flag = !((n1%num1 == start1 || (n1 + 1) % num1 == start1) && (n2%num2 == start2 || (n2 + 1) % num2 == start2));

  } while (flag);

  //最後兩個三角形的情況
  if ((n1 + 1) % num1 == start1 && (n2 + 1) % num2 == start2) {

	  boundary1->GetPoint(n1, p1);
	  boundary1->GetPoint((n1 + 1) % num1, p2);
	  boundary2->GetPoint(n2, p3);
	  boundary2->GetPoint((n2 + 1) % num2, p4);

	  d0 = vtkMath::Distance2BetweenPoints(p1, p4);
	  d1 = vtkMath::Distance2BetweenPoints(p2, p3);

	  if (d0 <= d1) {

		  pts[0] = n1%num1;
		  pts[1] = n2%num2 + num1;
		  pts[2] = (n2 + 1) % num2 + num1;
		  triangles->InsertNextCell(3, pts);

		  pts[0] = n1%num1;
		  pts[1] = (n2 + 1) % num2 + num1;
		  pts[2] = (n1 + 1) % num1;
		  triangles->InsertNextCell(3, pts);
	  }
	  else {

		  pts[0] = n1%num1;
		  pts[1] = n2%num2 + num1;
		  pts[2] = (n1 + 1) % num1;
		  triangles->InsertNextCell(3, pts);

		  pts[0] = (n1 + 1) % num1;
		  pts[1] = (n2 + 1) % num2 + num1;
		  pts[2] = n2%num2 + num1;
		  triangles->InsertNextCell(3, pts);
	  }

  }
  else {  // 最後一個三角形的情況

	  if (n1 == 0) {

		  pts[0] = n1%num1;
		  pts[1] = n2%num2 + num1;
		  pts[2] = (n2 + 1) % num2 + num1;

	  }
	  else {
		  pts[0] = n1%num1;
		  pts[1] = n2%num2 + num1;
		  pts[2] = (n1 + 1) % num1;
	  }
	  triangles->InsertNextCell(3, pts);
  }
  Mesh->BuildLinks();
  // combine two poly data
  // 上下三角網和側面三角網組合,生成封閉的體
  vtkSmartPointer<vtkAppendPolyData> appendFilter = vtkSmartPointer<vtkAppendPolyData>::New();
  appendFilter->AddInputData(sourceOne);
  appendFilter->AddInputData(sourceTwo);
  appendFilter->AddInputData(Mesh);
  appendFilter->Update();
  output->SetPoints(appendFilter->GetOutput()->GetPoints());
  output->SetPolys(appendFilter->GetOutput()->GetPolys());
  Mesh->Delete();

  return 1;
}

Delaunay2D.cxx

#include <vtkSmartPointer.h>

#include <vtkPoints.h>
#include <vtkPolyData.h>

#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkProperty.h>
#include <vtkNamedColors.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>

#include <vtkDelaunay2D.h>
#include <vtkVertexGlyphFilter.h>

#include "vtkTestAlgorithmFilter.h"

int main(int, char *[])
{
  /*
  * 模擬生成第一個三角網
  * 1. 隨機生成點集
  * 2. 獲取生成三角網的邊界環點集(有序)
  */
  vtkSmartPointer<vtkPoints> ps1 =vtkSmartPointer<vtkPoints>::New();
 
  unsigned int GridSize = 10;

  for(unsigned int x = 0; x < GridSize; x++)
  {
    for(unsigned int y = 0; y < GridSize; y++)
    {
		ps1->InsertNextPoint(x, y, vtkMath::Random(-.25, .25));
	}
  }
  vtkSmartPointer<vtkPolyData> polydata1 =vtkSmartPointer<vtkPolyData>::New();
  polydata1->SetPoints(ps1);

  vtkSmartPointer<vtkDelaunay2D> delaunay1 =vtkSmartPointer<vtkDelaunay2D>::New();
  delaunay1->SetInputData(polydata1);
  delaunay1->Update();

  vtkSmartPointer<vtkPoints> pts2 = vtkSmartPointer<vtkPoints>::New();
  GridSize = 9;
  for (unsigned int x =1; x < GridSize; x++)
  {
	  for (unsigned int y =1; y < GridSize; y++)
	  {
		  pts2->InsertNextPoint(x+ vtkMath::Random(-.25, 1.25), y+
			  vtkMath::Random(-.25, 1.25), vtkMath::Random(1.5, 1.75));
		 
	  }
  }

  vtkSmartPointer<vtkPolyData> polydata2 = vtkSmartPointer<vtkPolyData>::New();
  polydata2->SetPoints(pts2);
  vtkSmartPointer<vtkDelaunay2D> delaunay2 = vtkSmartPointer<vtkDelaunay2D>::New();
  delaunay2->SetInputData(polydata2);
  delaunay2->Update();

  // Visualize
  vtkSmartPointer<vtkTestAlgorithmFilter>testAlgorithm 
	  = vtkSmartPointer<vtkTestAlgorithmFilter>::New();
  testAlgorithm->SetInputData(0,delaunay1->GetOutput()); //
  testAlgorithm->SetInputData(1, delaunay2->GetOutput());
  testAlgorithm->Update();

  vtkSmartPointer<vtkNamedColors> colors =vtkSmartPointer<vtkNamedColors>::New();

  vtkSmartPointer<vtkPolyDataMapper> meshMapper =vtkSmartPointer<vtkPolyDataMapper>::New();
  meshMapper->SetInputConnection(testAlgorithm->GetOutputPort());

  vtkSmartPointer<vtkActor> meshActor =vtkSmartPointer<vtkActor>::New();
  meshActor->SetMapper(meshMapper);
  meshActor->GetProperty()->SetColor(colors->GetColor3d("Banana").GetData());
  meshActor->GetProperty()->EdgeVisibilityOn();

  vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter =vtkSmartPointer<vtkVertexGlyphFilter>::New();
  glyphFilter->SetInputData(testAlgorithm->GetOutput());

  vtkSmartPointer<vtkPolyDataMapper> pointMapper =vtkSmartPointer<vtkPolyDataMapper>::New();
  pointMapper->SetInputConnection(glyphFilter->GetOutputPort());

  vtkSmartPointer<vtkActor> pointActor =vtkSmartPointer<vtkActor>::New();
  pointActor->GetProperty()->SetColor(colors->GetColor3d("Tomato").GetData());
  pointActor->GetProperty()->SetPointSize(5);
  pointActor->SetMapper(pointMapper);

  vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
  vtkSmartPointer<vtkRenderWindow> renderWindow =vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->AddRenderer(renderer);
  vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
	  vtkSmartPointer<vtkRenderWindowInteractor>::New();
  renderWindowInteractor->SetRenderWindow(renderWindow);

  renderer->AddActor(meshActor);
  renderer->AddActor(pointActor);

  renderer->SetBackground(colors->GetColor3d("Mint").GetData());

  renderWindow->Render();
  renderWindowInteractor->Start();

  return EXIT_SUCCESS;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章