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

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

1 数据数组

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

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

void vtkDataArray::InsertTuple1(vtkIdType i, double value)
{int numComp = this->GetNumberOfComponents();if (numComp != 1){vtkErrorMacro("The number of components do not match the number requested: " << numComp << " != 1");}this->InsertTuple(i, &value);
}template <class ValueTypeT>
void vtkAOSDataArrayTemplate<ValueTypeT>::InsertTuple(vtkIdType tupleIdx, const double* tuple)
{if (this->EnsureAccessToTuple(tupleIdx)){// See note in SetTuple about std::copy vs for loops on MSVC.const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents;ValueTypeT* data = this->Buffer->GetBuffer() + valueIdx;for (int i = 0; i < this->NumberOfComponents; ++i){data[i] = static_cast<ValueType>(tuple[i]);}this->MaxId = std::max(this->MaxId, valueIdx + this->NumberOfComponents - 1);}
}template <class DerivedT, class ValueTypeT>
bool vtkGenericDataArray<DerivedT, ValueTypeT>::EnsureAccessToTuple(vtkIdType tupleIdx)
{if (tupleIdx < 0){return false;}vtkIdType minSize = (1 + tupleIdx) * this->NumberOfComponents;vtkIdType expectedMaxId = minSize - 1;if (this->MaxId < expectedMaxId){if (this->Size < minSize){if (!this->Resize(tupleIdx + 1)){return false;}}this->MaxId = expectedMaxId;}return true;
}

从代码可以看出,插入值采用的是数组指针偏移的方式。插入前,通过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))来间接表示单元的类型,下面通过一个例子来看看它的使用。

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
#include <vtkActor.h>
#include <vtkNamedColors.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSphereSource.h>
#include <vtkOpenGLRenderer.h>
#include <vtkOpenGLState.h>int main(int, char*[])
{vtkNew<vtkNamedColors> colors;static double pts[25][3] = {{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  }, {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  }, {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  }, {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  }, {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  }} ;static vtkIdType lines[80][2] = {{0, 5    }, {0, 1    }, {1, 6    }, {5, 6    }, {5, 1    }, {1, 6    }, {1, 2    }, {2, 7    }, {6, 7    }, {6, 2    }, {2, 7    }, {2, 3    }, {3, 8    }, {7, 8    }, {7, 3    }, {3, 8    }, {3, 4    }, {4, 9    }, {8, 9    }, {8, 4    }, {5, 10   }, {5, 6    }, {6, 11   }, {10, 11  }, {10, 6   }, {6, 11   }, {6, 7    }, {7, 12   }, {11, 12  }, {11, 7   }, {7, 12   }, {7, 8    }, {8, 13   }, {12, 13  }, {12, 8   }, {8, 13   }, {8, 9    }, {9, 14   }, {13, 14  }, {13, 9   }, {10, 15  }, {10, 11  }, {11, 16  }, {15, 16  }, {15, 11  }, {11, 16  }, {11, 12  }, {12, 17  }, {16, 17  }, {16, 12  }, {12, 17  }, {12, 13  }, {13, 18  }, {17, 18  }, {17, 13  }, {13, 18  }, {13, 14  }, {14, 19  }, {18, 19  }, {18, 14  }, {15, 20  }, {15, 16  }, {16, 21  }, {20, 21  }, {20, 16  }, {16, 21  }, {16, 17  }, {17, 22  }, {21, 22  }, {21, 17  }, {17, 22  }, {17, 18  }, {18, 23  }, {22, 23  }, {22, 18  }, {18, 23  }, {18, 19  }, {19, 24  }, {23, 24  }, {23, 19  }};static vtkIdType strips[32][3] = {{0, 5, 1    }, {5, 1, 6    }, {1, 6, 2    }, {6, 2, 7    }, {2, 7, 3    }, {7, 3, 8    }, {3, 8, 4    }, {8, 4, 9    }, {5, 10, 6   }, {10, 6, 11  }, {6, 11, 7   }, {11, 7, 12  }, {7, 12, 8   }, {12, 8, 13  }, {8, 13, 9   }, {13, 9, 14  }, {10, 15, 11 }, {15, 11, 16 }, {11, 16, 12 }, {16, 12, 17 }, {12, 17, 13 }, {17, 13, 18 }, {13, 18, 14 }, {18, 14, 19 }, {15, 20, 16 }, {20, 16, 21 }, {16, 21, 17 }, {21, 17, 22 }, {17, 22, 18 }, {22, 18, 23 }, {18, 23, 19 }, {23, 19, 24 }};vtkIdType numVerts = 25;vtkIdType numLines = 80;vtkIdType numStrips = 32;vtkIdType numCells = numVerts + numLines + numStrips;vtkIdType i;vtkPoints* points = vtkPoints::New();points->SetNumberOfPoints(25);for (i = 0; i < 25; i++){points->InsertPoint(i, pts[i]);}vtkSmartPointer<vtkPolyData> poly = vtkSmartPointer<vtkPolyData>::New();poly->AllocateExact(numCells, numCells);poly->SetPoints(points);points->Delete();for (i = 0; i < numVerts; i++){poly->InsertNextCell(VTK_VERTEX, 1, &i);}for (i = 0; i < numLines; i++){poly->InsertNextCell(VTK_LINE, 2, lines[i]);}for (i = 0; i < numStrips; i++){poly->InsertNextCell(VTK_TRIANGLE_STRIP, 3, strips[i]);}poly->BuildCells();vtkNew<vtkPolyDataMapper> mapper;mapper->SetInputData(poly);vtkNew<vtkActor> actor;actor->SetMapper(mapper);actor->GetProperty()->SetLineWidth(6);actor->GetProperty()->SetPointSize(25);actor->GetProperty()->SetRenderLinesAsTubes(1);actor->GetProperty()->SetRenderPointsAsSpheres(1);actor->GetProperty()->SetColor(colors->GetColor3d("Cornsilk").GetData());vtkNew<vtkRenderer> renderer;vtkNew<vtkRenderWindow> renderWindow;renderWindow->SetSize(500, 500);renderWindow->AddRenderer(renderer);vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;renderWindowInteractor->SetRenderWindow(renderWindow);renderer->AddActor(actor);renderer->SetBackground(colors->GetColor3d("DarkGreen").GetData());renderWindow->Render();renderWindowInteractor->Start();getchar();return EXIT_SUCCESS;
}

运行效果图如下:

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

vtkUnstructuredGrid

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

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkCellType.h>
#include <vtkDataSetMapper.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkUnstructuredGrid.h>int main(int, char*[])
{int i;static double x[27][3] = {{0, 0, 0}, {1, 0, 0}, {2, 0, 0}, {0, 1, 0}, {1, 1, 0}, {2, 1, 0},{0, 0, 1}, {1, 0, 1}, {2, 0, 1}, {0, 1, 1}, {1, 1, 1}, {2, 1, 1},{0, 1, 2}, {1, 1, 2}, {2, 1, 2}, {0, 1, 3}, {1, 1, 3}, {2, 1, 3},{0, 1, 4}, {1, 1, 4}, {2, 1, 4}, {0, 1, 5}, {1, 1, 5}, {2, 1, 5},{0, 1, 6}, {1, 1, 6}, {2, 1, 6}};static vtkIdType pts[12][8] = {{0, 1, 4, 3, 6, 7, 10, 9},      {1, 2, 5, 4, 7, 8, 11, 10},{6, 10, 9, 12, 0, 0, 0, 0},     {8, 11, 10, 14, 0, 0, 0, 0},{16, 17, 14, 13, 12, 15, 0, 0}, {18, 15, 19, 16, 20, 17, 0, 0},{22, 23, 20, 19, 0, 0, 0, 0},   {21, 22, 18, 0, 0, 0, 0, 0},{22, 19, 18, 0, 0, 0, 0, 0},    {23, 26, 0, 0, 0, 0, 0, 0},{21, 24, 0, 0, 0, 0, 0, 0},     {25, 0, 0, 0, 0, 0, 0, 0}};vtkNew<vtkNamedColors> colors;vtkNew<vtkRenderer> renderer;vtkNew<vtkRenderWindow> renWin;renWin->AddRenderer(renderer);vtkNew<vtkRenderWindowInteractor> iren;iren->SetRenderWindow(renWin);vtkNew<vtkPoints> points;for (i = 0; i < 27; i++) points->InsertPoint(i, x[i]);vtkNew<vtkUnstructuredGrid> ugrid;ugrid->Allocate(100);ugrid->InsertNextCell(VTK_HEXAHEDRON, 8, pts[0]);ugrid->InsertNextCell(VTK_HEXAHEDRON, 8, pts[1]);ugrid->InsertNextCell(VTK_TETRA, 4, pts[2]);ugrid->InsertNextCell(VTK_TETRA, 4, pts[3]);ugrid->InsertNextCell(VTK_POLYGON, 6, pts[4]);ugrid->InsertNextCell(VTK_TRIANGLE_STRIP, 6, pts[5]);ugrid->InsertNextCell(VTK_QUAD, 4, pts[6]);ugrid->InsertNextCell(VTK_TRIANGLE, 3, pts[7]);ugrid->InsertNextCell(VTK_TRIANGLE, 3, pts[8]);ugrid->InsertNextCell(VTK_LINE, 2, pts[9]);ugrid->InsertNextCell(VTK_LINE, 2, pts[10]);ugrid->InsertNextCell(VTK_VERTEX, 1, pts[11]);ugrid->SetPoints(points);vtkNew<vtkDataSetMapper> ugridMapper;ugridMapper->SetInputData(ugrid);vtkNew<vtkActor> ugridActor;ugridActor->SetMapper(ugridMapper);ugridActor->GetProperty()->SetColor(colors->GetColor3d("Peacock").GetData());ugridActor->GetProperty()->EdgeVisibilityOn();renderer->AddActor(ugridActor);renderer->SetBackground(colors->GetColor3d("Beige").GetData());renderer->ResetCamera();renderer->GetActiveCamera()->Elevation(60.0);renderer->GetActiveCamera()->Azimuth(30.0);renderer->GetActiveCamera()->Dolly(1.2);renWin->SetSize(640, 480);renWin->SetWindowName("UGrid)");// interact with datarenWin->Render();iren->Start();getchar();return EXIT_SUCCESS;
}

运行效果图如下:

3 数据模型

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

vtkPoints

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

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

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

  void InsertPoint(vtkIdType id, const float x[3]) VTK_EXPECTS(0 <= id){this->Data->InsertTuple(id, x);}void InsertPoint(vtkIdType id, const double x[3]) VTK_EXPECTS(0 <= id){this->Data->InsertTuple(id, x);}vtkIdType InsertNextPoint(const float x[3]) { return this->Data->InsertNextTuple(x); }vtkIdType InsertNextPoint(const double x[3]) { return this->Data->InsertNextTuple(x); }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列表。因此,对于由两个三角形、一个四边形和一条线组成的数据集,内部数组将如下所示:

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

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

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

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

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

/*--------------------------------------------------------------------------*/
/* Choose an implementation for vtkIdType.  */
#define VTK_HAS_ID_TYPE
#ifdef VTK_USE_64BIT_IDS
#if VTK_SIZEOF_LONG_LONG == 8
typedef long long vtkIdType;
#define VTK_ID_TYPE_IMPL VTK_LONG_LONG
#define VTK_SIZEOF_ID_TYPE VTK_SIZEOF_LONG_LONG
#define VTK_ID_MIN VTK_LONG_LONG_MIN
#define VTK_ID_MAX VTK_LONG_LONG_MAX
#define VTK_ID_TYPE_PRId "lld"
#elif VTK_SIZEOF_LONG == 8
typedef long vtkIdType;
#define VTK_ID_TYPE_IMPL VTK_LONG
#define VTK_SIZEOF_ID_TYPE VTK_SIZEOF_LONG
#define VTK_ID_MIN VTK_LONG_MIN
#define VTK_ID_MAX VTK_LONG_MAX
#define VTK_ID_TYPE_PRId "ld"
#else
#error "VTK_USE_64BIT_IDS is ON but no 64-bit integer type is available."
#endif
#else
typedef int vtkIdType;
#define VTK_ID_TYPE_IMPL VTK_INT
#define VTK_SIZEOF_ID_TYPE VTK_SIZEOF_INT
#define VTK_ID_MIN VTK_INT_MIN
#define VTK_ID_MAX VTK_INT_MAX
#define VTK_ID_TYPE_PRId "d"
#endif

InsertNextCell

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

vtkIdType vtkPolyData::InsertNextCell(int type, vtkIdList* pts)
{return this->InsertNextCell(type, static_cast<int>(pts->GetNumberOfIds()), pts->GetPointer(0));
}vtkIdType vtkPolyData::InsertNextCell(int type, int npts, const vtkIdType ptsIn[])
{
...// Insert next cell into the lookup map:TaggedCellId& tag = this->Cells->InsertNextCell(VTKCellType(type));vtkCellArray* cells = this->GetCellArrayInternal(tag);// Validate and update the internal cell id:const vtkIdType internalCellId = cells->InsertNextCell(npts, pts);if (internalCellId < 0){vtkErrorMacro("Internal error: Invalid cell id (" << internalCellId << ").");return -1;}
...// Return the dataset cell id:return this->Cells->GetNumberOfCells() - 1;
}inline vtkCellArray* vtkPolyData::GetCellArrayInternal(vtkPolyData::TaggedCellId tag)
{switch (tag.GetTarget()){case vtkPolyData_detail::Target::Verts:return this->Verts;case vtkPolyData_detail::Target::Lines:return this->Lines;case vtkPolyData_detail::Target::Polys:return this->Polys;case vtkPolyData_detail::Target::Strips:return this->Strips;}return nullptr; // unreachable
}

vtkPolyData::InsertNextCell函数中调用:

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

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

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

插入单元到vtkCellArray。

inline vtkIdType vtkCellArray::InsertNextCell(vtkIdType npts, const vtkIdType* pts)VTK_SIZEHINT(pts, npts)
{return this->Visit(vtkCellArray_detail::InsertNextCellImpl{}, npts, pts);
}template <typename Functor, typename... Args,typename = typename std::enable_if<!ReturnsVoid<Functor, Args...>::value>::type>
GetReturnType<Functor, Args...> Visit(Functor&& functor, Args&&... args)
{if (this->Storage.Is64Bit()){// If you get an error on the next line, a call to Visit(functor, Args...)// is being called with arguments that do not match the functor's call// signature. See the Visit documentation for details.return functor(this->Storage.GetArrays64(), std::forward<Args>(args)...);}else{// If you get an error on the next line, a call to Visit(functor, Args...)// is being called with arguments that do not match the functor's call// signature. See the Visit documentation for details.return functor(this->Storage.GetArrays32(), std::forward<Args>(args)...);}
}

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

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

struct InsertNextCellImpl
{// Insert full celltemplate <typename CellStateT>vtkIdType operator()(CellStateT& state, const vtkIdType npts, const vtkIdType pts[]){using ValueType = typename CellStateT::ValueType;auto* conn = state.GetConnectivity();auto* offsets = state.GetOffsets();const vtkIdType cellId = offsets->GetNumberOfValues() - 1;offsets->InsertNextValue(static_cast<ValueType>(conn->GetNumberOfValues() + npts));for (vtkIdType i = 0; i < npts; ++i){conn->InsertNextValue(static_cast<ValueType>(pts[i]));}return cellId;}
...
};

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

  struct Storage{// Union type that switches 32 and 64 bit array storageunion ArraySwitch {ArraySwitch() = default;  // handled by Storage~ArraySwitch() = default; // handle by StorageVisitState<ArrayType32>* Int32;VisitState<ArrayType64>* Int64;};Storage(){
#ifdef VTK_USE_MEMKINDthis->Arrays =static_cast<ArraySwitch*>(vtkObjectBase::GetCurrentMallocFunction()(sizeof(ArraySwitch)));
#elsethis->Arrays = new ArraySwitch;
#endif// Default to the compile-time setting:
#ifdef VTK_USE_64BIT_IDSthis->Arrays->Int64 = new VisitState<ArrayType64>;this->StorageIs64Bit = true;#else // VTK_USE_64BIT_IDSthis->Arrays->Int32 = new VisitState<ArrayType32>;this->StorageIs64Bit = false;#endif // VTK_USE_64BIT_IDS
#ifdef VTK_USE_MEMKINDif (vtkObjectBase::GetUsingMemkind()){this->IsInMemkind = true;}
#else(void)this->IsInMemkind; // comp warning workaround
#endif}...// Get the VisitState for 32-bit arraysVisitState<ArrayType32>& GetArrays32(){assert(!this->StorageIs64Bit);return *this->Arrays->Int32;}const VisitState<ArrayType32>& GetArrays32() const{assert(!this->StorageIs64Bit);return *this->Arrays->Int32;}// Get the VisitState for 64-bit arraysVisitState<ArrayType64>& GetArrays64(){assert(this->StorageIs64Bit);return *this->Arrays->Int64;}const VisitState<ArrayType64>& GetArrays64() const{assert(this->StorageIs64Bit);return *this->Arrays->Int64;}private:// Access restricted to ensure proper union construction/destruction thru// API.ArraySwitch* Arrays;bool StorageIs64Bit;bool IsInMemkind = false;};

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

  template <typename ArrayT>struct VisitState{using ArrayType = ArrayT;using ValueType = typename ArrayType::ValueType;using CellRangeType = decltype(vtk::DataArrayValueRange<1>(std::declval<ArrayType>()));// We can't just use is_same here, since binary compatible representations// (e.g. int and long) are distinct types. Instead, ensure that ValueType// is a signed integer the same size as vtkIdType.// If this value is true, ValueType pointers may be safely converted to// vtkIdType pointers via reinterpret cast.static constexpr bool ValueTypeIsSameAsIdType = std::is_integral<ValueType>::value &&std::is_signed<ValueType>::value && (sizeof(ValueType) == sizeof(vtkIdType));ArrayType* GetOffsets() { return this->Offsets; }const ArrayType* GetOffsets() const { return this->Offsets; }ArrayType* GetConnectivity() { return this->Connectivity; }const ArrayType* GetConnectivity() const { return this->Connectivity; }...friend class vtkCellArray;protected:VisitState(){this->Connectivity = vtkSmartPointer<ArrayType>::New();this->Offsets = vtkSmartPointer<ArrayType>::New();this->Offsets->InsertNextValue(0);if (vtkObjectBase::GetUsingMemkind()){this->IsInMemkind = true;}}vtkSmartPointer<ArrayType> Connectivity;vtkSmartPointer<ArrayType> Offsets;...};

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

4 数据属性

vtkPointData 和 vtkCellData

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

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
#include <vtkActor.h>
#include <vtkArrowSource.h>
#include <vtkCamera.h>
#include <vtkGlyph3D.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkCubeSource.h>
#include <vtkCellArray.h>
#include <vtkFloatArray.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkPoints.h>int main(int, char*[])
{vtkNew<vtkNamedColors> colors;std::array<std::array<double, 3>, 8> pts = {{{{0, 0, 0}},{{1, 0, 0}},{{1, 1, 0}},{{0, 1, 0}},{{0, 0, 1}},{{1, 0, 1}},{{1, 1, 1}},{{0, 1, 1}}}};// The ordering of the corner points on each face.std::array<std::array<vtkIdType, 4>, 6> ordering = {{{{0, 3, 2, 1}},{{4, 5, 6, 7}},{{0, 1, 5, 4}},{{1, 2, 6, 5}},{{2, 3, 7, 6}},{{3, 0, 4, 7}}}};std::array<std::array<double, 3>, 8> vertex_normals;     for(auto i = 0; i < 8; ++i){for(auto j = 0; j < 3; ++j){vertex_normals[i][j] = pts[i][j] - 0.5;}}       // We'll create the building blocks of polydata including data attributes.vtkNew<vtkPolyData> cube;vtkNew<vtkPoints> points;vtkNew<vtkCellArray> polys;vtkNew<vtkFloatArray> vertex_normals_array;vtkNew<vtkFloatArray> scalar_array;// Load the point, cell, and data attributes.for (auto i = 0ul; i < pts.size(); ++i){points->InsertPoint(i, pts[i].data());}for (auto&& i : ordering){polys->InsertNextCell(vtkIdType(i.size()), i.data());}vertex_normals_array->SetNumberOfComponents(3);for(auto i = 0; i < vertex_normals.size(); ++i){vertex_normals_array->InsertNextTuple3(vertex_normals[i][0], vertex_normals[i][1], vertex_normals[i][2]);}scalar_array->SetNumberOfComponents(1);for(auto i = 0; i < ordering.size(); ++i){scalar_array->InsertNextTuple1(i);}// We now assign the pieces to the vtkPolyData.cube->SetPoints(points);cube->SetPolys(polys);cube->GetPointData()->SetNormals(vertex_normals_array);cube->GetCellData()->SetScalars(scalar_array);vtkNew<vtkPolyData> input;input->ShallowCopy(cube);vtkNew<vtkArrowSource> arrowSource;vtkNew<vtkGlyph3D> glyph3D;glyph3D->SetSourceConnection(arrowSource->GetOutputPort());glyph3D->SetInputData(input);glyph3D->ScalingOn();glyph3D->SetVectorModeToUseNormal();glyph3D->SetScaleFactor(0.25);glyph3D->Update();// VisualizevtkNew<vtkPolyDataMapper> mapper;mapper->SetInputConnection(glyph3D->GetOutputPort());vtkNew<vtkPolyDataMapper> cube_mapper;cube_mapper->SetInputData(cube);cube_mapper->SetScalarRange(cube->GetScalarRange());vtkNew<vtkActor> actor;actor->SetMapper(mapper);actor->GetProperty()->SetColor(colors->GetColor3d("Gold").GetData());vtkNew<vtkActor> cube_actor;cube_actor->SetMapper(cube_mapper);vtkNew<vtkRenderer> renderer;vtkNew<vtkRenderWindow> renderWindow;renderWindow->AddRenderer(renderer);renderWindow->SetWindowName("OrientedGlyphs");vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;renderWindowInteractor->SetRenderWindow(renderWindow);renderer->AddActor(actor);renderer->AddActor(cube_actor);renderer->SetBackground(colors->GetColor3d("DarkGreen").GetData());renderWindow->Render();renderWindowInteractor->Start();getchar();return EXIT_SUCCESS;
}

运行效果如下:

上面的代码中:

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

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

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

  // Always keep NUM_ATTRIBUTES as the last entryenum AttributeTypes{SCALARS = 0,VECTORS = 1,NORMALS = 2,TCOORDS = 3,TENSORS = 4,GLOBALIDS = 5,PEDIGREEIDS = 6,EDGEFLAG = 7,TANGENTS = 8,RATIONALWEIGHTS = 9,HIGHERORDERDEGREES = 10,NUM_ATTRIBUTES};

Ghost属性

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

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

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

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

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

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
#include "vtkActor.h"
#include "vtkCellData.h"
#include "vtkCellType.h"
#include "vtkDataSetSurfaceFilter.h"
#include "vtkGeometryFilter.h"
#include "vtkNew.h"
#include "vtkPoints.h"
#include "vtkPolyDataMapper.h"
#include "vtkRegressionTestImage.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSmartPointer.h"
#include "vtkUnsignedCharArray.h"
#include "vtkUnstructuredGrid.h"
#include "vtkFloatArray.h"
#include "vtkColorTransferFunction.h"
#include "vtkTextActor.h"
#include "vtkTextProperty.h"
#include "vtkCamera.h"
#include "vtkCallbackCommand.h"
#include "vtkRendererCollection.h"
#include "vtkActor2DCollection.h"void CallbackFunction(vtkObject* caller, long unsigned int eventId,void* clientData, void* callData)
{vtkRenderWindowInteractor* iren = static_cast<vtkRenderWindowInteractor*>(caller);vtkRenderer* renderer = iren->GetRenderWindow()->GetRenderers()->GetFirstRenderer();if(clientData == nullptr)return;auto points = static_cast<vtkPoints*>(clientData);auto ac = renderer->GetActors2D();vtkActor2D* anActor;vtkCollectionSimpleIterator ait;for (ac->InitTraversal(ait); (anActor = ac->GetNextActor2D(ait));){auto ta = vtkTextActor::SafeDownCast(anActor);if(ta == nullptr) continue;std::string text = ta->GetInput();int idx = std::stoi(text);auto pt = points->GetPoint(idx);renderer->WorldToDisplay(pt[0], pt[1], pt[2]);ta->SetDisplayPosition(pt[0], pt[1]);}
}int main(int argc, char* argv[])
{vtkNew<vtkPoints> points;points->InsertPoint(0, 0, 0, 0);points->InsertPoint(1, 1, 0, 0);points->InsertPoint(2, 0.5, 1, 0);points->InsertPoint(3, 0.5, 0.5, 1);points->InsertPoint(4, 0.5, -1, 0);points->InsertPoint(5, 0.5, -0.5, 1);vtkIdType v[3][4] = { { 0, 1, 2, 3 }, { 0, 4, 1, 5 }, { 5, 3, 1, 0 } };//vtkIdType v[3][4] = { { 0, 1, 2, 3 }, { 5, 3, 1, 0 }, { 0, 4, 1, 5 } };vtkSmartPointer<vtkUnstructuredGrid> grid = vtkSmartPointer<vtkUnstructuredGrid>::New();grid->InsertNextCell(VTK_TETRA, 4, v[0]);grid->InsertNextCell(VTK_TETRA, 4, v[1]);grid->InsertNextCell(VTK_TETRA, 4, v[2]);grid->SetPoints(points);vtkNew<vtkFloatArray> cell_scalar_array;cell_scalar_array->SetNumberOfComponents(1);cell_scalar_array->InsertNextTuple1(0);cell_scalar_array->InsertNextTuple1(3);cell_scalar_array->InsertNextTuple1(7);grid->GetCellData()->SetScalars(cell_scalar_array);// vtkNew<vtkUnsignedCharArray> ghosts;// ghosts->InsertNextValue(0);// ghosts->InsertNextValue(1);// ghosts->InsertNextValue(2);// ghosts->SetName(vtkDataSetAttributes::GhostArrayName()); // grid->GetCellData()->AddArray(ghosts);// this filter removes the ghost cellsvtkNew<vtkGeometryFilter> surfaces;surfaces->SetInputData(grid);surfaces->Update(); vtkNew<vtkColorTransferFunction> clrTransferFunc;clrTransferFunc->SetColorSpaceToRGB();clrTransferFunc->AddRGBPoint(0, 1, 0, 0);clrTransferFunc->AddRGBPoint(3, 0, 1, 0);clrTransferFunc->AddRGBPoint(7, 0, 1, 1);vtkNew<vtkPolyDataMapper> mapper;mapper->SetInputConnection(surfaces->GetOutputPort());mapper->SetScalarRange(grid->GetScalarRange());mapper->SetLookupTable(clrTransferFunc);vtkNew<vtkActor> actor;actor->SetMapper(mapper);vtkNew<vtkCamera> camera;camera->SetPosition(0, 0, 5);camera->SetFocalPoint(0, 0, 0);vtkNew<vtkRenderer> renderer;renderer->SetActiveCamera(camera);renderer->ResetCamera();renderer->AddActor(actor);vtkNew<vtkRenderWindow> renwin;renwin->AddRenderer(renderer);renwin->SetSize(500, 500);vtkNew<vtkRenderWindowInteractor> iren;iren->SetRenderWindow(renwin);iren->Initialize();vtkNew<vtkCallbackCommand> callback;callback->SetCallback(CallbackFunction);callback->SetClientData(points);iren->AddObserver(vtkCommand::LeftButtonPressEvent, callback);for(int i = 0; i < points->GetNumberOfPoints(); ++i){auto pt = points->GetPoint(i);std::string text = std::to_string(i);vtkNew<vtkTextActor> ta;ta->SetInput(text.c_str());ta->GetTextProperty()->SetColor(0.5, 1.0, 0.0);renderer->WorldToDisplay(pt[0], pt[1], pt[2]);ta->SetDisplayPosition(pt[0], pt[1]);ta->GetTextProperty()->SetFontSize(32);renderer->AddActor(ta.Get());}renwin->Render();iren->Start();getchar();return EXIT_SUCCESS;
}

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/3907.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nginx 交叉编译,启动报错nginx: [emerg] getgrnam(“nogroup“) failed 的原因和解决办法

目录 一、错误提示 nginx: [emerg] getgrnam("nogroup") failed二、解决办法三、测试 一、错误提示 nginx: [emerg] getgrnam(“nogroup”) failed nginx 交叉编译&#xff0c;在开发板上启动报错 nginx: [emerg] getgrnam("nogroup") failed二、解决办法…

笔记本硬盘坏了怎么把数据弄出来 笔记本硬盘数据恢复一般需要多少钱

现在办公基本都离不开笔记本电脑&#xff0c;就连学生写作业也大多是都在电脑上完成。硬盘作为电脑存储的重要组成部分&#xff0c;承载着存储文件和各类软件的重任。如果硬盘出现故障&#xff0c;基本上这台电脑就无法正常工作&#xff0c;同时我们可能面临丢失很多重要的数据…

电气设备绝缘的高电压试验(二)——高电压的测量

本篇为本科课程《高电压工程基础》的笔记。 本篇为这一单元的第二篇笔记。上一篇传送门。 稳态高电压的测量 稳态高电压主要指的是工频交流高压和直流高压。高压测量系统常常含有转换装置、转换装置到试验品之间的引线、接地连线、低压测量回路和测量仪表等。 实验室测量方…

【python】语言学习笔记--用来记录总结

请问以下变量哪些是tuple类型&#xff1a; a ()b (1)c [2]d (3,)e (4,5,6)answer在Python中&#xff0c;元组&#xff08;tuple&#xff09;是由逗号分隔的一组值组成的有序序列&#xff0c;通常用圆括号括起来。让我们逐个检查变量&#xff0c;看哪些是元组类型&#xff…

【UE5.1 C++】提升编译速度

步骤 1. 在“C:\Users\用户\AppData\Roaming\Unreal Engine\UnrealBuildTool”目录下找到“BuildConfiguration.xml”文件 打开“BuildConfiguration.xml”&#xff0c;添加如下部分内容 <?xml version"1.0" encoding"utf-8" ?> <Configuratio…

JavaSE字节缓冲流

欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间与空间的平衡&#xff0c;0与1的延伸的后端开发者。 博客特色&#xff1a; 在我的博客中&a…

中移在线ChinaMobile系统单机和分布式应用的登录校验解决方案

单机的Tomcat应用登录校验&#xff1a; 用户首次登录成功后&#xff0c;服务端会创建一个Session会话&#xff0c;客户端会生成一个sessionid&#xff0c;客户端会把sessionid保存到cookie里&#xff0c;每次请求都携带这个sessionid&#xff0c;服务端通过校验来判断是拦截还是…

Vuforia AR篇(四)— AR虚拟按钮

目录 前言一、创建虚拟按钮二、创建脚本三、效果 前言 在当今互联网和移动设备普及的背景下&#xff0c;**增强现实&#xff08;AR&#xff09;**技术正迅速成为连接现实世界与数字信息的重要桥梁。AR虚拟按钮作为这一技术的创新应用&#xff0c;不仅提供了一种全新的用户交互…

mac上安装Tomcat

1. 简介 Tomcat 是一个开源的 Java 服务器&#xff0c;它实现了 Java Servlet、JavaServer Pages&#xff08;JSP&#xff09;和Java WebSocket 技术。Tomcat 是 Apache 软件基金会的一个项目&#xff0c;是一个轻量级、高性能的 Web 容器。作为一个 Web 服务器&#xff0c;To…

go设计模式之抽象工厂模式

抽象工厂模式 提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定它们具体的类。 工厂方法模式通过引入工厂等级结构&#xff0c;解决了简单工厂模式中工厂类职责太重的问题&#xff0c;但由于工厂方法模式中的每个工厂只生产一类产品&#xff0c;可能会导致…

Angular创建项目

Angular创建项目 文章目录 Angular创建项目1. 创建项目1.1 直接安装1.2 跳过npm i安装 2. 运行程序 1. 创建项目 ng new 项目名称 1.1 直接安装 ng new angulardemo --同时会安装依赖包&#xff0c;执行的命令就是npm i 1.2 跳过npm i安装 ng new angulardemo --skip-inst…

Pytorch 的实际应用 学习笔记

一. 模型的下载 weights为false时则为没有提前经过训练的模型&#xff0c;为true时则经过了提前训练 vgg16_false torchvision.models.vgg16(weightsFalse) vgg16_true torchvision.models.vgg16(weightsTrue) 打印 二. 模型的修改 &#xff08;1&#xff09;添加操作 …

RabbitMQ中的交换机类型

交换机类型 可以看到&#xff0c;在订阅模型中&#xff0c;多了一个exchange角色&#xff0c;而且过程略有变化&#xff1a; Publisher&#xff1a;生产者&#xff0c;不再发送消息到队列中&#xff0c;而是发给交换机 Exchange&#xff1a;交换机&#xff0c;一方面&#xff…

欧科云链:为什么减半对比特币生态的影响正在逐步“减弱”?

出品&#xff5c;OKG Research 作者&#xff5c;Jason Jiang 欧科云链OKLink数据显示&#xff0c;比特币于区块高度840000&#xff08;北京时间2024年4月20日8:09&#xff09;成功完成第四次减半&#xff0c;比特币挖矿奖励正式由6.25BTC减少至3.125BTC。此次减半之后&#x…

Spring MVC系列之九大核心组件

概述 Spring MVC是面试必问知识点其一&#xff0c;Spring MVC知识体系庞杂&#xff0c;有以下九大核心组件&#xff1a; HandlerMappingHandlerAdapterHandlerExceptionResolverViewResolverRequestToViewNameTranslatorLocaleResolverThemeResolverMultipartResolverFlashMa…

中电金信:深度解析|数字化营销运营体系搭建

如何更好更快地梳理好体系搭建思路&#xff0c;稳步实现落地&#xff1f;下文将为大家明确搭建的推进步骤、执行要点&#xff0c;帮助商业银行理顺数字化营销运营体系的“点”“线”“面”~ 与所有转型的曲折、阵痛等特征一样&#xff0c;商业银行构建数字化营销运营体系过程中…

URL路由基础与Django处理请求的过程分析

1. URL路由基础 对于高质量的Web应用来讲&#xff0c;使用简洁、优雅的URL设计模式非常有必要。Django框架允许设计人员自由地设计URL模式&#xff0c;而不用受到框架本身的约束。对于URL路由来讲&#xff0c;其主要实现了Web服务的入口。用户通过浏览器发送过来的任何请求&am…

PyQt5中的QTablewidget

环境 PyQt5 VSCode Qt Designer生成界面 在VSCode的资源管理器中&#xff0c;右键选择 PYQT:New Form&#xff0c;打开Qt Designer 选择新建Dialog without Buttons&#xff0c;点击 创建 在左侧的Item Widgets中将 Table Widget拖入Dialog窗体中。 得到界面 将文件保存…

CH4INRULZ-v1靶机练习实践报告

CH4INRULZ-v1靶机练习实践报告 1 安装靶机 靶机是.ova文件&#xff0c;需要用VirtualBox打开&#xff0c;但我习惯于使用VMWare,因此修改靶机文件&#xff0c;使其适用于VMWare打开。 解压ova文件&#xff0c;得到.ovf文件和.vmdk文件。直接用VMWare打开.ovf文件即可。 2 夺…

Oceanbase体验之(一)运维管理工具OCP部署(社区版4.2.2)

资源规划建议 ocp主机1台 内存:64G CPU1:2C及以上 硬盘大于500G observer服务器3台 内存32G CPU&#xff1a;4C以上 硬盘大于1T 建议存储硬盘与操作系统硬盘隔开实现IO隔离 一、OBD、OCP安装包准备 [rootobserver /]# chown -R admin:admin /software/ [rootobserver /]# …