当前位置:   article > 正文

VTK----VTK数据结构详解3(代码篇)

VTK----VTK数据结构详解3(代码篇)

上篇文章(VTK----VTK数据结构详解(计算机篇)-CSDN博客)从计算机数据结构(数组、链表等)的角度对数据数组数据对象数据属性的实现原理进行了说明,下面从代码的层面详细说明它们的使用及相关实现逻辑。

1 数据数组

vtkFloatArray为例,下面是它的使用及其VTK内部实现的代码:

vtkNew<vtkFloatArray> scalars;
scalars->InsertTuple1(0, 1);

  1. void vtkDataArray::InsertTuple1(vtkIdType i, double value)
  2. {
  3. int numComp = this->GetNumberOfComponents();
  4. if (numComp != 1)
  5. {
  6. vtkErrorMacro(
  7. "The number of components do not match the number requested: " << numComp << " != 1");
  8. }
  9. this->InsertTuple(i, &value);
  10. }
  11. template <class ValueTypeT>
  12. void vtkAOSDataArrayTemplate<ValueTypeT>::InsertTuple(vtkIdType tupleIdx, const double* tuple)
  13. {
  14. if (this->EnsureAccessToTuple(tupleIdx))
  15. {
  16. // See note in SetTuple about std::copy vs for loops on MSVC.
  17. const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents;
  18. ValueTypeT* data = this->Buffer->GetBuffer() + valueIdx;
  19. for (int i = 0; i < this->NumberOfComponents; ++i)
  20. {
  21. data[i] = static_cast<ValueType>(tuple[i]);
  22. }
  23. this->MaxId = std::max(this->MaxId, valueIdx + this->NumberOfComponents - 1);
  24. }
  25. }
  26. template <class DerivedT, class ValueTypeT>
  27. bool vtkGenericDataArray<DerivedT, ValueTypeT>::EnsureAccessToTuple(vtkIdType tupleIdx)
  28. {
  29. if (tupleIdx < 0)
  30. {
  31. return false;
  32. }
  33. vtkIdType minSize = (1 + tupleIdx) * this->NumberOfComponents;
  34. vtkIdType expectedMaxId = minSize - 1;
  35. if (this->MaxId < expectedMaxId)
  36. {
  37. if (this->Size < minSize)
  38. {
  39. if (!this->Resize(tupleIdx + 1))
  40. {
  41. return false;
  42. }
  43. }
  44. this->MaxId = expectedMaxId;
  45. }
  46. return true;
  47. }

从代码可以看出,插入值采用的是数组指针偏移的方式。插入前,通过EnsureAccessToTuple函数先检查是否需要Resize。插入时,指针偏移tupleIdx * NumberOfComponents,tupleIdx是当前准备在元组中插入的索引位置,NumberOfComponents是元组大小(可以理解为子一级的数组,子一级数组大小是固定值),上面的例子调用的是InsertTuple1,对应是一元组,所以NumberOfComponents等于1。在vtkDataArray.h中可以看到其提供了InsertTuple1、InsertTuple2、InsertTuple3、InsertTuple4、InsertTuple6、InsertTuple9这些接口分别用于插入对应大小的元组元素。

2 数据对象

vtkPolyData

上一篇文章提到vtkPolyData通过维护四个单独的列表(顶点(vertices)、线(lines)、多边形(polygons)和三角形带(triangle strips))来间接表示单元的类型,下面通过一个例子来看看它的使用。

  1. #include <vtkAutoInit.h>
  2. VTK_MODULE_INIT(vtkRenderingOpenGL2);
  3. VTK_MODULE_INIT(vtkInteractionStyle);
  4. VTK_MODULE_INIT(vtkRenderingFreeType);
  5. VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
  6. #include <vtkActor.h>
  7. #include <vtkNamedColors.h>
  8. #include <vtkPolyData.h>
  9. #include <vtkPolyDataMapper.h>
  10. #include <vtkProperty.h>
  11. #include <vtkRenderWindow.h>
  12. #include <vtkRenderWindowInteractor.h>
  13. #include <vtkRenderer.h>
  14. #include <vtkSphereSource.h>
  15. #include <vtkOpenGLRenderer.h>
  16. #include <vtkOpenGLState.h>
  17. int main(int, char*[])
  18. {
  19. vtkNew<vtkNamedColors> colors;
  20. static double pts[25][3] = {
  21. {0.00, 1.00, 0.00 }, {0.00, 0.71, 0.71 }, {0.00, 0.00, 1.00 }, {0.00, -0.71, 0.71 }, {0.00, -1.00, 0.00 },
  22. {0.00, 1.00, 0.00 }, {0.71, 0.71, 0.00 }, {1.00, 0.00, 0.00 }, {0.71, -0.71, 0.00 }, {0.00, -1.00, 0.00 },
  23. {0.00, 1.00, 0.00 }, {0.00, 0.71, -0.71 }, {0.00, 0.00, -1.00 }, {0.00, -0.71, -0.71 }, {0.00, -1.00, 0.00 },
  24. {0.00, 1.00, 0.00 }, {-0.71, 0.71, 0.00 }, {-1.00, 0.00, 0.00 }, {-0.71, -0.71, 0.00 }, {0.00, -1.00, 0.00 },
  25. {0.00, 1.00, 0.00 }, {0.00, 0.71, 0.71 }, {0.00, 0.00, 1.00 }, {0.00, -0.71, 0.71 }, {0.00, -1.00, 0.00 }
  26. } ;
  27. static vtkIdType lines[80][2] = {
  28. {0, 5 }, {0, 1 }, {1, 6 }, {5, 6 }, {5, 1 }, {1, 6 }, {1, 2 }, {2, 7 },
  29. {6, 7 }, {6, 2 }, {2, 7 }, {2, 3 }, {3, 8 }, {7, 8 }, {7, 3 }, {3, 8 },
  30. {3, 4 }, {4, 9 }, {8, 9 }, {8, 4 }, {5, 10 }, {5, 6 }, {6, 11 }, {10, 11 },
  31. {10, 6 }, {6, 11 }, {6, 7 }, {7, 12 }, {11, 12 }, {11, 7 }, {7, 12 }, {7, 8 },
  32. {8, 13 }, {12, 13 }, {12, 8 }, {8, 13 }, {8, 9 }, {9, 14 }, {13, 14 }, {13, 9 },
  33. {10, 15 }, {10, 11 }, {11, 16 }, {15, 16 }, {15, 11 }, {11, 16 }, {11, 12 }, {12, 17 },
  34. {16, 17 }, {16, 12 }, {12, 17 }, {12, 13 }, {13, 18 }, {17, 18 }, {17, 13 }, {13, 18 },
  35. {13, 14 }, {14, 19 }, {18, 19 }, {18, 14 }, {15, 20 }, {15, 16 }, {16, 21 }, {20, 21 },
  36. {20, 16 }, {16, 21 }, {16, 17 }, {17, 22 }, {21, 22 }, {21, 17 }, {17, 22 }, {17, 18 },
  37. {18, 23 }, {22, 23 }, {22, 18 }, {18, 23 }, {18, 19 }, {19, 24 }, {23, 24 }, {23, 19 }
  38. };
  39. static vtkIdType strips[32][3] = {
  40. {0, 5, 1 }, {5, 1, 6 }, {1, 6, 2 }, {6, 2, 7 }, {2, 7, 3 }, {7, 3, 8 }, {3, 8, 4 }, {8, 4, 9 },
  41. {5, 10, 6 }, {10, 6, 11 }, {6, 11, 7 }, {11, 7, 12 }, {7, 12, 8 }, {12, 8, 13 }, {8, 13, 9 }, {13, 9, 14 },
  42. {10, 15, 11 }, {15, 11, 16 }, {11, 16, 12 }, {16, 12, 17 }, {12, 17, 13 }, {17, 13, 18 }, {13, 18, 14 }, {18, 14, 19 },
  43. {15, 20, 16 }, {20, 16, 21 }, {16, 21, 17 }, {21, 17, 22 }, {17, 22, 18 }, {22, 18, 23 }, {18, 23, 19 }, {23, 19, 24 }
  44. };
  45. vtkIdType numVerts = 25;
  46. vtkIdType numLines = 80;
  47. vtkIdType numStrips = 32;
  48. vtkIdType numCells = numVerts + numLines + numStrips;
  49. vtkIdType i;
  50. vtkPoints* points = vtkPoints::New();
  51. points->SetNumberOfPoints(25);
  52. for (i = 0; i < 25; i++)
  53. {
  54. points->InsertPoint(i, pts[i]);
  55. }
  56. vtkSmartPointer<vtkPolyData> poly = vtkSmartPointer<vtkPolyData>::New();
  57. poly->AllocateExact(numCells, numCells);
  58. poly->SetPoints(points);
  59. points->Delete();
  60. for (i = 0; i < numVerts; i++)
  61. {
  62. poly->InsertNextCell(VTK_VERTEX, 1, &i);
  63. }
  64. for (i = 0; i < numLines; i++)
  65. {
  66. poly->InsertNextCell(VTK_LINE, 2, lines[i]);
  67. }
  68. for (i = 0; i < numStrips; i++)
  69. {
  70. poly->InsertNextCell(VTK_TRIANGLE_STRIP, 3, strips[i]);
  71. }
  72. poly->BuildCells();
  73. vtkNew<vtkPolyDataMapper> mapper;
  74. mapper->SetInputData(poly);
  75. vtkNew<vtkActor> actor;
  76. actor->SetMapper(mapper);
  77. actor->GetProperty()->SetLineWidth(6);
  78. actor->GetProperty()->SetPointSize(25);
  79. actor->GetProperty()->SetRenderLinesAsTubes(1);
  80. actor->GetProperty()->SetRenderPointsAsSpheres(1);
  81. actor->GetProperty()->SetColor(colors->GetColor3d("Cornsilk").GetData());
  82. vtkNew<vtkRenderer> renderer;
  83. vtkNew<vtkRenderWindow> renderWindow;
  84. renderWindow->SetSize(500, 500);
  85. renderWindow->AddRenderer(renderer);
  86. vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
  87. renderWindowInteractor->SetRenderWindow(renderWindow);
  88. renderer->AddActor(actor);
  89. renderer->SetBackground(colors->GetColor3d("DarkGreen").GetData());
  90. renderWindow->Render();
  91. renderWindowInteractor->Start();
  92. getchar();
  93. return EXIT_SUCCESS;
  94. }

运行效果图如下:

从图中可以看到绘制了点、线、由三角网渲染出来的面。

vtkUnstructuredGrid

vtkUnstructuredGrid可以表示所有单元类型(规则和不规则的),下面通过一个例子来看看它的使用。

  1. #include <vtkAutoInit.h>
  2. VTK_MODULE_INIT(vtkRenderingOpenGL2);
  3. VTK_MODULE_INIT(vtkInteractionStyle);
  4. VTK_MODULE_INIT(vtkRenderingFreeType);
  5. VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
  6. #include <vtkActor.h>
  7. #include <vtkCamera.h>
  8. #include <vtkCellType.h>
  9. #include <vtkDataSetMapper.h>
  10. #include <vtkNamedColors.h>
  11. #include <vtkNew.h>
  12. #include <vtkPoints.h>
  13. #include <vtkProperty.h>
  14. #include <vtkRenderWindow.h>
  15. #include <vtkRenderWindowInteractor.h>
  16. #include <vtkRenderer.h>
  17. #include <vtkUnstructuredGrid.h>
  18. int main(int, char*[])
  19. {
  20. int i;
  21. static double x[27][3] = {
  22. {0, 0, 0}, {1, 0, 0}, {2, 0, 0}, {0, 1, 0}, {1, 1, 0}, {2, 1, 0},
  23. {0, 0, 1}, {1, 0, 1}, {2, 0, 1}, {0, 1, 1}, {1, 1, 1}, {2, 1, 1},
  24. {0, 1, 2}, {1, 1, 2}, {2, 1, 2}, {0, 1, 3}, {1, 1, 3}, {2, 1, 3},
  25. {0, 1, 4}, {1, 1, 4}, {2, 1, 4}, {0, 1, 5}, {1, 1, 5}, {2, 1, 5},
  26. {0, 1, 6}, {1, 1, 6}, {2, 1, 6}};
  27. static vtkIdType pts[12][8] = {
  28. {0, 1, 4, 3, 6, 7, 10, 9}, {1, 2, 5, 4, 7, 8, 11, 10},
  29. {6, 10, 9, 12, 0, 0, 0, 0}, {8, 11, 10, 14, 0, 0, 0, 0},
  30. {16, 17, 14, 13, 12, 15, 0, 0}, {18, 15, 19, 16, 20, 17, 0, 0},
  31. {22, 23, 20, 19, 0, 0, 0, 0}, {21, 22, 18, 0, 0, 0, 0, 0},
  32. {22, 19, 18, 0, 0, 0, 0, 0}, {23, 26, 0, 0, 0, 0, 0, 0},
  33. {21, 24, 0, 0, 0, 0, 0, 0}, {25, 0, 0, 0, 0, 0, 0, 0}};
  34. vtkNew<vtkNamedColors> colors;
  35. vtkNew<vtkRenderer> renderer;
  36. vtkNew<vtkRenderWindow> renWin;
  37. renWin->AddRenderer(renderer);
  38. vtkNew<vtkRenderWindowInteractor> iren;
  39. iren->SetRenderWindow(renWin);
  40. vtkNew<vtkPoints> points;
  41. for (i = 0; i < 27; i++) points->InsertPoint(i, x[i]);
  42. vtkNew<vtkUnstructuredGrid> ugrid;
  43. ugrid->Allocate(100);
  44. ugrid->InsertNextCell(VTK_HEXAHEDRON, 8, pts[0]);
  45. ugrid->InsertNextCell(VTK_HEXAHEDRON, 8, pts[1]);
  46. ugrid->InsertNextCell(VTK_TETRA, 4, pts[2]);
  47. ugrid->InsertNextCell(VTK_TETRA, 4, pts[3]);
  48. ugrid->InsertNextCell(VTK_POLYGON, 6, pts[4]);
  49. ugrid->InsertNextCell(VTK_TRIANGLE_STRIP, 6, pts[5]);
  50. ugrid->InsertNextCell(VTK_QUAD, 4, pts[6]);
  51. ugrid->InsertNextCell(VTK_TRIANGLE, 3, pts[7]);
  52. ugrid->InsertNextCell(VTK_TRIANGLE, 3, pts[8]);
  53. ugrid->InsertNextCell(VTK_LINE, 2, pts[9]);
  54. ugrid->InsertNextCell(VTK_LINE, 2, pts[10]);
  55. ugrid->InsertNextCell(VTK_VERTEX, 1, pts[11]);
  56. ugrid->SetPoints(points);
  57. vtkNew<vtkDataSetMapper> ugridMapper;
  58. ugridMapper->SetInputData(ugrid);
  59. vtkNew<vtkActor> ugridActor;
  60. ugridActor->SetMapper(ugridMapper);
  61. ugridActor->GetProperty()->SetColor(colors->GetColor3d("Peacock").GetData());
  62. ugridActor->GetProperty()->EdgeVisibilityOn();
  63. renderer->AddActor(ugridActor);
  64. renderer->SetBackground(colors->GetColor3d("Beige").GetData());
  65. renderer->ResetCamera();
  66. renderer->GetActiveCamera()->Elevation(60.0);
  67. renderer->GetActiveCamera()->Azimuth(30.0);
  68. renderer->GetActiveCamera()->Dolly(1.2);
  69. renWin->SetSize(640, 480);
  70. renWin->SetWindowName("UGrid)");
  71. // interact with data
  72. renWin->Render();
  73. iren->Start();
  74. getchar();
  75. return EXIT_SUCCESS;
  76. }

运行效果图如下:

3 数据模型

VTK中数据模型由单元(cell)点(point)组成,点对应vtkPoints类,单元对应vtkCellArray类。

vtkPoints

vtkPoints用来表示和操作3D点。vtkPoints的数据模型是可通过(点或单元)id访问的vx-vy-vz三元组数组。

  1. vtkPoints::vtkPoints(int dataType)
  2. {
  3. this->Data = vtkFloatArray::New();
  4. this->Data->Register(this);
  5. this->Data->Delete();
  6. this->SetDataType(dataType);
  7. this->Data->SetNumberOfComponents(3);
  8. this->Data->SetName("Points");
  9. this->Bounds[0] = this->Bounds[2] = this->Bounds[4] = VTK_DOUBLE_MAX;
  10. this->Bounds[1] = this->Bounds[3] = this->Bounds[5] = -VTK_DOUBLE_MAX;
  11. }

从vtkPoints类构造函数中的代码可以看出,它创建了一个vtkFloatArray数据数组,NumberOfComponents为3表示其为一个三元组。

  1. void InsertPoint(vtkIdType id, const float x[3]) VTK_EXPECTS(0 <= id)
  2. {
  3. this->Data->InsertTuple(id, x);
  4. }
  5. void InsertPoint(vtkIdType id, const double x[3]) VTK_EXPECTS(0 <= id)
  6. {
  7. this->Data->InsertTuple(id, x);
  8. }
  9. vtkIdType InsertNextPoint(const float x[3]) { return this->Data->InsertNextTuple(x); }
  10. vtkIdType InsertNextPoint(const double x[3]) { return this->Data->InsertNextTuple(x); }
  11. vtkIdType InsertNextPoint(double x, double y, double z);

InsertPoint和InsertNextPoint内部调用的是数据数组的InsertTuple和InsertNextTuple函数。

vtkCellArray

vtkCellArray用来表示单元内部连接性的对象。其将数据集的拓扑结构存为显示连接性(connectivity)表,表中列出组成每个单元的点ID。

在vtkCellArray内部,连接性表表示为两个数组:Offsets(偏移)Connectivity(连接性)。Offsets是一个[numCells+1]值的数组,指示Connectivity数组中每个单元点开始的索引,最后一个值始终是Connectivity数组的长度。Connectivity数组存每个单元的点ID列表。因此,对于由两个三角形、一个四边形和一条线组成的数据集,内部数组将如下所示:

  1. Topology:
  2. ---------
  3. Cell 0: Triangle | point ids: {0, 1, 2}
  4. Cell 1: Triangle | point ids: {5, 7, 2}
  5. Cell 2: Quad | point ids: {3, 4, 6, 7}
  6. Cell 4: Line | point ids: {5, 8}
  7. vtkCellArray (current):
  8. -----------------------
  9. Offsets: {0, 3, 6, 10, 12}
  10. Connectivity: {0, 1, 2, 5, 7, 2, 3, 4, 6, 7, 5, 8}

虽然此类提供了遍历方法(旧的InitTraversal()、GetNextCell()方法和较新的方法GetCellAtId()),但这些方法通常不是线程安全的。最好使用本地线程安全的vtkCellArrayInterator对象,可以通过以下方式获取该对象:

  1. auto iter = vtk::TakeSmartPointer(cellArray->NewIterator());
  2. for (iter->GoToFirstCell(); !iter->IsDoneWithTraversal(); iter->GoToNextCell())
  3. {
  4. // do work with iter
  5. }

(但请注意,根据内部存的类型和结构,由于额外的数据复制,单元数组迭代器可能比直接遍历单元数组慢得多。另外,如果vtkCellArray内部存储被修改,迭代器可能变得无效。)

vtkCellArray内部的数组可以存32或64位的值,尽管大多数API更喜欢使用vtkIdType来对应数组中的元素。允许将64位存储于32位vtkIdType一起使用,但太大而无法容纳32位有符号整数的值在通过API访问时将被截断。(特定的内部存储类型对性能有影响,具体取决于vtkIdType。如果内部存储等效于vtkIdType,则返回指向点"id"数组的指针的方法可以共享内部存储,否则必须执行复制内存)。

  1. /*--------------------------------------------------------------------------*/
  2. /* Choose an implementation for vtkIdType. */
  3. #define VTK_HAS_ID_TYPE
  4. #ifdef VTK_USE_64BIT_IDS
  5. #if VTK_SIZEOF_LONG_LONG == 8
  6. typedef long long vtkIdType;
  7. #define VTK_ID_TYPE_IMPL VTK_LONG_LONG
  8. #define VTK_SIZEOF_ID_TYPE VTK_SIZEOF_LONG_LONG
  9. #define VTK_ID_MIN VTK_LONG_LONG_MIN
  10. #define VTK_ID_MAX VTK_LONG_LONG_MAX
  11. #define VTK_ID_TYPE_PRId "lld"
  12. #elif VTK_SIZEOF_LONG == 8
  13. typedef long vtkIdType;
  14. #define VTK_ID_TYPE_IMPL VTK_LONG
  15. #define VTK_SIZEOF_ID_TYPE VTK_SIZEOF_LONG
  16. #define VTK_ID_MIN VTK_LONG_MIN
  17. #define VTK_ID_MAX VTK_LONG_MAX
  18. #define VTK_ID_TYPE_PRId "ld"
  19. #else
  20. #error "VTK_USE_64BIT_IDS is ON but no 64-bit integer type is available."
  21. #endif
  22. #else
  23. typedef int vtkIdType;
  24. #define VTK_ID_TYPE_IMPL VTK_INT
  25. #define VTK_SIZEOF_ID_TYPE VTK_SIZEOF_INT
  26. #define VTK_ID_MIN VTK_INT_MIN
  27. #define VTK_ID_MAX VTK_INT_MAX
  28. #define VTK_ID_TYPE_PRId "d"
  29. #endif

InsertNextCell

InsertNextCell是用来插入单元的函数,各种数据对象中都有实现该接口,用以构造本对象中的各种类型的单元。下面我们通过vtkPolyData中InsertNextCell函数的内部实现代码看下它是如何构造单元的。

  1. vtkIdType vtkPolyData::InsertNextCell(int type, vtkIdList* pts)
  2. {
  3. return this->InsertNextCell(type, static_cast<int>(pts->GetNumberOfIds()), pts->GetPointer(0));
  4. }
  5. vtkIdType vtkPolyData::InsertNextCell(int type, int npts, const vtkIdType ptsIn[])
  6. {
  7. ...
  8. // Insert next cell into the lookup map:
  9. TaggedCellId& tag = this->Cells->InsertNextCell(VTKCellType(type));
  10. vtkCellArray* cells = this->GetCellArrayInternal(tag);
  11. // Validate and update the internal cell id:
  12. const vtkIdType internalCellId = cells->InsertNextCell(npts, pts);
  13. if (internalCellId < 0)
  14. {
  15. vtkErrorMacro("Internal error: Invalid cell id (" << internalCellId << ").");
  16. return -1;
  17. }
  18. ...
  19. // Return the dataset cell id:
  20. return this->Cells->GetNumberOfCells() - 1;
  21. }
  22. inline vtkCellArray* vtkPolyData::GetCellArrayInternal(vtkPolyData::TaggedCellId tag)
  23. {
  24. switch (tag.GetTarget())
  25. {
  26. case vtkPolyData_detail::Target::Verts:
  27. return this->Verts;
  28. case vtkPolyData_detail::Target::Lines:
  29. return this->Lines;
  30. case vtkPolyData_detail::Target::Polys:
  31. return this->Polys;
  32. case vtkPolyData_detail::Target::Strips:
  33. return this->Strips;
  34. }
  35. return nullptr; // unreachable
  36. }

vtkPolyData::InsertNextCell函数中调用:

vtkCellArray* cells = this->GetCellArrayInternal(tag);

获取当前要插入单元的类型对应的vtkCellArray。然后调用:

const vtkIdType internalCellId = cells->InsertNextCell(npts, pts);

插入单元到vtkCellArray。

  1. inline vtkIdType vtkCellArray::InsertNextCell(vtkIdType npts, const vtkIdType* pts)
  2. VTK_SIZEHINT(pts, npts)
  3. {
  4. return this->Visit(vtkCellArray_detail::InsertNextCellImpl{}, npts, pts);
  5. }
  6. template <typename Functor, typename... Args,
  7. typename = typename std::enable_if<!ReturnsVoid<Functor, Args...>::value>::type>
  8. GetReturnType<Functor, Args...> Visit(Functor&& functor, Args&&... args)
  9. {
  10. if (this->Storage.Is64Bit())
  11. {
  12. // If you get an error on the next line, a call to Visit(functor, Args...)
  13. // is being called with arguments that do not match the functor's call
  14. // signature. See the Visit documentation for details.
  15. return functor(this->Storage.GetArrays64(), std::forward<Args>(args)...);
  16. }
  17. else
  18. {
  19. // If you get an error on the next line, a call to Visit(functor, Args...)
  20. // is being called with arguments that do not match the functor's call
  21. // signature. See the Visit documentation for details.
  22. return functor(this->Storage.GetArrays32(), std::forward<Args>(args)...);
  23. }
  24. }

Visit(Functor&& functor, Args&&... args)函数的第一个参数是一个InsertNextCellImpl对象(&&可以理解为std::move操作),第二个参数是一个可变参数列表。

functor(this->Storage.GetArrays64(), std::forward<Args>(args)...);调用的是InsertNextCellImpl中()操作符函数:

  1. struct InsertNextCellImpl
  2. {
  3. // Insert full cell
  4. template <typename CellStateT>
  5. vtkIdType operator()(CellStateT& state, const vtkIdType npts, const vtkIdType pts[])
  6. {
  7. using ValueType = typename CellStateT::ValueType;
  8. auto* conn = state.GetConnectivity();
  9. auto* offsets = state.GetOffsets();
  10. const vtkIdType cellId = offsets->GetNumberOfValues() - 1;
  11. offsets->InsertNextValue(static_cast<ValueType>(conn->GetNumberOfValues() + npts));
  12. for (vtkIdType i = 0; i < npts; ++i)
  13. {
  14. conn->InsertNextValue(static_cast<ValueType>(pts[i]));
  15. }
  16. return cellId;
  17. }
  18. ...
  19. };

operator()函数的第一个参数通过this->Storage.GetArrays64()获得。

  1. struct Storage
  2. {
  3. // Union type that switches 32 and 64 bit array storage
  4. union ArraySwitch {
  5. ArraySwitch() = default; // handled by Storage
  6. ~ArraySwitch() = default; // handle by Storage
  7. VisitState<ArrayType32>* Int32;
  8. VisitState<ArrayType64>* Int64;
  9. };
  10. Storage()
  11. {
  12. #ifdef VTK_USE_MEMKIND
  13. this->Arrays =
  14. static_cast<ArraySwitch*>(vtkObjectBase::GetCurrentMallocFunction()(sizeof(ArraySwitch)));
  15. #else
  16. this->Arrays = new ArraySwitch;
  17. #endif
  18. // Default to the compile-time setting:
  19. #ifdef VTK_USE_64BIT_IDS
  20. this->Arrays->Int64 = new VisitState<ArrayType64>;
  21. this->StorageIs64Bit = true;
  22. #else // VTK_USE_64BIT_IDS
  23. this->Arrays->Int32 = new VisitState<ArrayType32>;
  24. this->StorageIs64Bit = false;
  25. #endif // VTK_USE_64BIT_IDS
  26. #ifdef VTK_USE_MEMKIND
  27. if (vtkObjectBase::GetUsingMemkind())
  28. {
  29. this->IsInMemkind = true;
  30. }
  31. #else
  32. (void)this->IsInMemkind; // comp warning workaround
  33. #endif
  34. }
  35. ...
  36. // Get the VisitState for 32-bit arrays
  37. VisitState<ArrayType32>& GetArrays32()
  38. {
  39. assert(!this->StorageIs64Bit);
  40. return *this->Arrays->Int32;
  41. }
  42. const VisitState<ArrayType32>& GetArrays32() const
  43. {
  44. assert(!this->StorageIs64Bit);
  45. return *this->Arrays->Int32;
  46. }
  47. // Get the VisitState for 64-bit arrays
  48. VisitState<ArrayType64>& GetArrays64()
  49. {
  50. assert(this->StorageIs64Bit);
  51. return *this->Arrays->Int64;
  52. }
  53. const VisitState<ArrayType64>& GetArrays64() const
  54. {
  55. assert(this->StorageIs64Bit);
  56. return *this->Arrays->Int64;
  57. }
  58. private:
  59. // Access restricted to ensure proper union construction/destruction thru
  60. // API.
  61. ArraySwitch* Arrays;
  62. bool StorageIs64Bit;
  63. bool IsInMemkind = false;
  64. };

Storage内通过联合体(union)管理一个32位或64位的VisitState。

  1. template <typename ArrayT>
  2. struct VisitState
  3. {
  4. using ArrayType = ArrayT;
  5. using ValueType = typename ArrayType::ValueType;
  6. using CellRangeType = decltype(vtk::DataArrayValueRange<1>(std::declval<ArrayType>()));
  7. // We can't just use is_same here, since binary compatible representations
  8. // (e.g. int and long) are distinct types. Instead, ensure that ValueType
  9. // is a signed integer the same size as vtkIdType.
  10. // If this value is true, ValueType pointers may be safely converted to
  11. // vtkIdType pointers via reinterpret cast.
  12. static constexpr bool ValueTypeIsSameAsIdType = std::is_integral<ValueType>::value &&
  13. std::is_signed<ValueType>::value && (sizeof(ValueType) == sizeof(vtkIdType));
  14. ArrayType* GetOffsets() { return this->Offsets; }
  15. const ArrayType* GetOffsets() const { return this->Offsets; }
  16. ArrayType* GetConnectivity() { return this->Connectivity; }
  17. const ArrayType* GetConnectivity() const { return this->Connectivity; }
  18. ...
  19. friend class vtkCellArray;
  20. protected:
  21. VisitState()
  22. {
  23. this->Connectivity = vtkSmartPointer<ArrayType>::New();
  24. this->Offsets = vtkSmartPointer<ArrayType>::New();
  25. this->Offsets->InsertNextValue(0);
  26. if (vtkObjectBase::GetUsingMemkind())
  27. {
  28. this->IsInMemkind = true;
  29. }
  30. }
  31. vtkSmartPointer<ArrayType> Connectivity;
  32. vtkSmartPointer<ArrayType> Offsets;
  33. ...
  34. };

回到InsertNextCellImpl的operator()函数。我们可以看到,函数中根据InsertNextCell传入的单元类型对应的点数(npts)在进行遍历,依次插入单元对应的点的索引集合;将conn->GetNumberOfValues() + npts(当前已经插入的索引值总数+要插入单元的元组大小)的值作为偏移值插入Offsets数组中。

4 数据属性

vtkPointData 和 vtkCellData

上一篇文章讲到数据属性通常与点和单元关联关联的(也可以通过GetFieldData()关联到整个数据模型),VTK中使用vtkPointData和vtkCellData分别表示数据点和单元的属性,它们都是vtkFieldData的子类。下面我们通过一个例子来看下它们的使用。

  1. #include <vtkAutoInit.h>
  2. VTK_MODULE_INIT(vtkRenderingOpenGL2);
  3. VTK_MODULE_INIT(vtkInteractionStyle);
  4. VTK_MODULE_INIT(vtkRenderingFreeType);
  5. VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
  6. #include <vtkActor.h>
  7. #include <vtkArrowSource.h>
  8. #include <vtkCamera.h>
  9. #include <vtkGlyph3D.h>
  10. #include <vtkNamedColors.h>
  11. #include <vtkNew.h>
  12. #include <vtkPolyData.h>
  13. #include <vtkPolyDataMapper.h>
  14. #include <vtkProperty.h>
  15. #include <vtkRenderWindow.h>
  16. #include <vtkRenderWindowInteractor.h>
  17. #include <vtkRenderer.h>
  18. #include <vtkCubeSource.h>
  19. #include <vtkCellArray.h>
  20. #include <vtkFloatArray.h>
  21. #include <vtkPointData.h>
  22. #include <vtkCellData.h>
  23. #include <vtkPoints.h>
  24. int main(int, char*[])
  25. {
  26. vtkNew<vtkNamedColors> colors;
  27. std::array<std::array<double, 3>, 8> pts = {{{{0, 0, 0}},
  28. {{1, 0, 0}},
  29. {{1, 1, 0}},
  30. {{0, 1, 0}},
  31. {{0, 0, 1}},
  32. {{1, 0, 1}},
  33. {{1, 1, 1}},
  34. {{0, 1, 1}}}};
  35. // The ordering of the corner points on each face.
  36. std::array<std::array<vtkIdType, 4>, 6> ordering = {{{{0, 3, 2, 1}},
  37. {{4, 5, 6, 7}},
  38. {{0, 1, 5, 4}},
  39. {{1, 2, 6, 5}},
  40. {{2, 3, 7, 6}},
  41. {{3, 0, 4, 7}}}};
  42. std::array<std::array<double, 3>, 8> vertex_normals;
  43. for(auto i = 0; i < 8; ++i)
  44. {
  45. for(auto j = 0; j < 3; ++j)
  46. {
  47. vertex_normals[i][j] = pts[i][j] - 0.5;
  48. }
  49. }
  50. // We'll create the building blocks of polydata including data attributes.
  51. vtkNew<vtkPolyData> cube;
  52. vtkNew<vtkPoints> points;
  53. vtkNew<vtkCellArray> polys;
  54. vtkNew<vtkFloatArray> vertex_normals_array;
  55. vtkNew<vtkFloatArray> scalar_array;
  56. // Load the point, cell, and data attributes.
  57. for (auto i = 0ul; i < pts.size(); ++i)
  58. {
  59. points->InsertPoint(i, pts[i].data());
  60. }
  61. for (auto&& i : ordering)
  62. {
  63. polys->InsertNextCell(vtkIdType(i.size()), i.data());
  64. }
  65. vertex_normals_array->SetNumberOfComponents(3);
  66. for(auto i = 0; i < vertex_normals.size(); ++i)
  67. {
  68. vertex_normals_array->InsertNextTuple3(vertex_normals[i][0],
  69. vertex_normals[i][1],
  70. vertex_normals[i][2]);
  71. }
  72. scalar_array->SetNumberOfComponents(1);
  73. for(auto i = 0; i < ordering.size(); ++i)
  74. {
  75. scalar_array->InsertNextTuple1(i);
  76. }
  77. // We now assign the pieces to the vtkPolyData.
  78. cube->SetPoints(points);
  79. cube->SetPolys(polys);
  80. cube->GetPointData()->SetNormals(vertex_normals_array);
  81. cube->GetCellData()->SetScalars(scalar_array);
  82. vtkNew<vtkPolyData> input;
  83. input->ShallowCopy(cube);
  84. vtkNew<vtkArrowSource> arrowSource;
  85. vtkNew<vtkGlyph3D> glyph3D;
  86. glyph3D->SetSourceConnection(arrowSource->GetOutputPort());
  87. glyph3D->SetInputData(input);
  88. glyph3D->ScalingOn();
  89. glyph3D->SetVectorModeToUseNormal();
  90. glyph3D->SetScaleFactor(0.25);
  91. glyph3D->Update();
  92. // Visualize
  93. vtkNew<vtkPolyDataMapper> mapper;
  94. mapper->SetInputConnection(glyph3D->GetOutputPort());
  95. vtkNew<vtkPolyDataMapper> cube_mapper;
  96. cube_mapper->SetInputData(cube);
  97. cube_mapper->SetScalarRange(cube->GetScalarRange());
  98. vtkNew<vtkActor> actor;
  99. actor->SetMapper(mapper);
  100. actor->GetProperty()->SetColor(colors->GetColor3d("Gold").GetData());
  101. vtkNew<vtkActor> cube_actor;
  102. cube_actor->SetMapper(cube_mapper);
  103. vtkNew<vtkRenderer> renderer;
  104. vtkNew<vtkRenderWindow> renderWindow;
  105. renderWindow->AddRenderer(renderer);
  106. renderWindow->SetWindowName("OrientedGlyphs");
  107. vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
  108. renderWindowInteractor->SetRenderWindow(renderWindow);
  109. renderer->AddActor(actor);
  110. renderer->AddActor(cube_actor);
  111. renderer->SetBackground(colors->GetColor3d("DarkGreen").GetData());
  112. renderWindow->Render();
  113. renderWindowInteractor->Start();
  114. getchar();
  115. return EXIT_SUCCESS;
  116. }

运行效果如下:

上面的代码中:

cube->GetPointData()->SetNormals(vertex_normals_array);将法向量数据数组设置给点,所以从图中可以看到箭头是在点的位置进行显示。

cube->GetCellData()->SetScalars(scalar_array);将标量数据数组设置给单元,可以看到每个单元(面)根据标量显示不同的颜色。

数据属性不仅仅只包含标量、向量,其所能支持的类型包括以下这些:

  1. // Always keep NUM_ATTRIBUTES as the last entry
  2. enum AttributeTypes
  3. {
  4. SCALARS = 0,
  5. VECTORS = 1,
  6. NORMALS = 2,
  7. TCOORDS = 3,
  8. TENSORS = 4,
  9. GLOBALIDS = 5,
  10. PEDIGREEIDS = 6,
  11. EDGEFLAG = 7,
  12. TANGENTS = 8,
  13. RATIONALWEIGHTS = 9,
  14. HIGHERORDERDEGREES = 10,
  15. NUM_ATTRIBUTES
  16. };

Ghost属性

考虑如下图所示的提取外表面(face)的操作。外表面操作用于识别没有本地邻居的所有面。当我们把这些面片放置一起时,我们发现一些面被错误地识别为外部面。当两个相邻的单元被放置在单独的处理中时,这些面的错误识别就会发生。

上图提取外部面结果错误是因为处理中丢失了一些重要的全局信息,这些单独的处理过程不需要所有的数据,但需要一些不属于它们自己数据,即它们需要知道相邻的其他分区中的单元。

这个问题可以通过引入Ghost单元来解决这个局部/全局问题。Ghost单元是属于数据的一个分区并重复在其他分区上的单元。Ghost单元的引入是通过领域信息进行的,并按层次进行组织。对于给定的分区,与分区中的单元相邻但不属于分区本身的任何单元都是Ghost单元1。与不属于层次1或原始分区层次1的Ghost单元相邻的任何单元都处于层次2。递归定义更深的层次。

将Ghost引用到提取外表面的示例中,效果如下图,图中某些面仍然被分类为外表面,但是,所有这些面都附着在Ghost单元上。这些Ghost面很容易被剔除,最终结果就是合适的外表面。

下面通过一个例子来看看VTK中Ghost单元的使用:

  1. #include <vtkAutoInit.h>
  2. VTK_MODULE_INIT(vtkRenderingOpenGL2);
  3. VTK_MODULE_INIT(vtkInteractionStyle);
  4. VTK_MODULE_INIT(vtkRenderingFreeType);
  5. VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
  6. #include "vtkActor.h"
  7. #include "vtkCellData.h"
  8. #include "vtkCellType.h"
  9. #include "vtkDataSetSurfaceFilter.h"
  10. #include "vtkGeometryFilter.h"
  11. #include "vtkNew.h"
  12. #include "vtkPoints.h"
  13. #include "vtkPolyDataMapper.h"
  14. #include "vtkRegressionTestImage.h"
  15. #include "vtkRenderWindow.h"
  16. #include "vtkRenderWindowInteractor.h"
  17. #include "vtkRenderer.h"
  18. #include "vtkSmartPointer.h"
  19. #include "vtkUnsignedCharArray.h"
  20. #include "vtkUnstructuredGrid.h"
  21. #include "vtkFloatArray.h"
  22. #include "vtkColorTransferFunction.h"
  23. #include "vtkTextActor.h"
  24. #include "vtkTextProperty.h"
  25. #include "vtkCamera.h"
  26. #include "vtkCallbackCommand.h"
  27. #include "vtkRendererCollection.h"
  28. #include "vtkActor2DCollection.h"
  29. void CallbackFunction(vtkObject* caller, long unsigned int eventId,
  30. void* clientData, void* callData)
  31. {
  32. vtkRenderWindowInteractor* iren = static_cast<vtkRenderWindowInteractor*>(caller);
  33. vtkRenderer* renderer = iren->GetRenderWindow()->GetRenderers()->GetFirstRenderer();
  34. if(clientData == nullptr)
  35. return;
  36. auto points = static_cast<vtkPoints*>(clientData);
  37. auto ac = renderer->GetActors2D();
  38. vtkActor2D* anActor;
  39. vtkCollectionSimpleIterator ait;
  40. for (ac->InitTraversal(ait); (anActor = ac->GetNextActor2D(ait));)
  41. {
  42. auto ta = vtkTextActor::SafeDownCast(anActor);
  43. if(ta == nullptr) continue;
  44. std::string text = ta->GetInput();
  45. int idx = std::stoi(text);
  46. auto pt = points->GetPoint(idx);
  47. renderer->WorldToDisplay(pt[0], pt[1], pt[2]);
  48. ta->SetDisplayPosition(pt[0], pt[1]);
  49. }
  50. }
  51. int main(int argc, char* argv[])
  52. {
  53. vtkNew<vtkPoints> points;
  54. points->InsertPoint(0, 0, 0, 0);
  55. points->InsertPoint(1, 1, 0, 0);
  56. points->InsertPoint(2, 0.5, 1, 0);
  57. points->InsertPoint(3, 0.5, 0.5, 1);
  58. points->InsertPoint(4, 0.5, -1, 0);
  59. points->InsertPoint(5, 0.5, -0.5, 1);
  60. vtkIdType v[3][4] = { { 0, 1, 2, 3 }, { 0, 4, 1, 5 }, { 5, 3, 1, 0 } };
  61. //vtkIdType v[3][4] = { { 0, 1, 2, 3 }, { 5, 3, 1, 0 }, { 0, 4, 1, 5 } };
  62. vtkSmartPointer<vtkUnstructuredGrid> grid = vtkSmartPointer<vtkUnstructuredGrid>::New();
  63. grid->InsertNextCell(VTK_TETRA, 4, v[0]);
  64. grid->InsertNextCell(VTK_TETRA, 4, v[1]);
  65. grid->InsertNextCell(VTK_TETRA, 4, v[2]);
  66. grid->SetPoints(points);
  67. vtkNew<vtkFloatArray> cell_scalar_array;
  68. cell_scalar_array->SetNumberOfComponents(1);
  69. cell_scalar_array->InsertNextTuple1(0);
  70. cell_scalar_array->InsertNextTuple1(3);
  71. cell_scalar_array->InsertNextTuple1(7);
  72. grid->GetCellData()->SetScalars(cell_scalar_array);
  73. // vtkNew<vtkUnsignedCharArray> ghosts;
  74. // ghosts->InsertNextValue(0);
  75. // ghosts->InsertNextValue(1);
  76. // ghosts->InsertNextValue(2);
  77. // ghosts->SetName(vtkDataSetAttributes::GhostArrayName());
  78. // grid->GetCellData()->AddArray(ghosts);
  79. // this filter removes the ghost cells
  80. vtkNew<vtkGeometryFilter> surfaces;
  81. surfaces->SetInputData(grid);
  82. surfaces->Update();
  83. vtkNew<vtkColorTransferFunction> clrTransferFunc;
  84. clrTransferFunc->SetColorSpaceToRGB();
  85. clrTransferFunc->AddRGBPoint(0, 1, 0, 0);
  86. clrTransferFunc->AddRGBPoint(3, 0, 1, 0);
  87. clrTransferFunc->AddRGBPoint(7, 0, 1, 1);
  88. vtkNew<vtkPolyDataMapper> mapper;
  89. mapper->SetInputConnection(surfaces->GetOutputPort());
  90. mapper->SetScalarRange(grid->GetScalarRange());
  91. mapper->SetLookupTable(clrTransferFunc);
  92. vtkNew<vtkActor> actor;
  93. actor->SetMapper(mapper);
  94. vtkNew<vtkCamera> camera;
  95. camera->SetPosition(0, 0, 5);
  96. camera->SetFocalPoint(0, 0, 0);
  97. vtkNew<vtkRenderer> renderer;
  98. renderer->SetActiveCamera(camera);
  99. renderer->ResetCamera();
  100. renderer->AddActor(actor);
  101. vtkNew<vtkRenderWindow> renwin;
  102. renwin->AddRenderer(renderer);
  103. renwin->SetSize(500, 500);
  104. vtkNew<vtkRenderWindowInteractor> iren;
  105. iren->SetRenderWindow(renwin);
  106. iren->Initialize();
  107. vtkNew<vtkCallbackCommand> callback;
  108. callback->SetCallback(CallbackFunction);
  109. callback->SetClientData(points);
  110. iren->AddObserver(vtkCommand::LeftButtonPressEvent, callback);
  111. for(int i = 0; i < points->GetNumberOfPoints(); ++i)
  112. {
  113. auto pt = points->GetPoint(i);
  114. std::string text = std::to_string(i);
  115. vtkNew<vtkTextActor> ta;
  116. ta->SetInput(text.c_str());
  117. ta->GetTextProperty()->SetColor(0.5, 1.0, 0.0);
  118. renderer->WorldToDisplay(pt[0], pt[1], pt[2]);
  119. ta->SetDisplayPosition(pt[0], pt[1]);
  120. ta->GetTextProperty()->SetFontSize(32);
  121. renderer->AddActor(ta.Get());
  122. }
  123. renwin->Render();
  124. iren->Start();
  125. getchar();
  126. return EXIT_SUCCESS;
  127. }

没应用ghost单元代码的情况下,效果如下图:

应用ghost单元代码的情况下,效果如下图:

可以看到vtkGeometryFilter移除了第二个Ghost单元。将第二个单元和第三个单元顺序换一下的效果图如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/515486
推荐阅读
相关标签
  

闽ICP备14008679号