作为一款开源跨平台的数据可视化代码库,VTK以其清晰的流水线工作方式、丰富的后处理算法、异种渲染/交互方式,而被众多CAx软件选作后处理实施方案。而异种渲染/交互方式的实现,主要是倚重于VTK的类型系统,因此,有必要对这个问题予以研究分析。
另外,结合前面对FreeCAD类型系统的分析,本文旨在导出类型系统在设计与实现等方面的(非)技术约束。
本文涉及的主要知识点包括,
- Factory Method Pattern
- Type Registration
注1:限于研究水平,分析难免不当,欢迎批评指正。
注2:文章内容会不定期更新。
一、相关组件
1.1 vtkObjectBase
vtkObjectBase定义了类型信息、引用计数等相关接口,而类型信息主要是基于其内部维护的类型名称字符串。
1.2 vtkObject
vtkObject派生于vtkObjectBase,增加了对象动态创建接口。
// Macro to use when you are a direct child class of vtkObjectBase, instead
// of vtkTypeMacro. This is required to properly specify NewInstanceInternal
// as a virtual method.
// It is used to determine whether a class is the same class or a subclass
// of the named class.#define vtkBaseTypeMacro(thisClass, superclass) \vtkAbstractTypeMacro(thisClass, superclass); \\
protected: \virtual vtkObjectBase* NewInstanceInternal() const { return thisClass::New(); } \\
public:
// Same as vtkTypeMacro, but adapted for cases where thisClass is abstract.
#define vtkAbstractTypeMacro(thisClass, superclass) \vtkAbstractTypeMacroWithNewInstanceType(thisClass, superclass, thisClass, #thisClass); \\
public:
// Allows definition of vtkObject API such that NewInstance may return a
// superclass of thisClass.
#define vtkAbstractTypeMacroWithNewInstanceType( \thisClass, superclass, instanceType, thisClassName) \
protected: \const char* GetClassNameInternal() const override { return thisClassName; } \\
public: \typedef superclass Superclass; \static vtkTypeBool IsTypeOf(const char* type) \{ \if (!strcmp(thisClassName, type)) \{ \return 1; \} \return superclass::IsTypeOf(type); \} \vtkTypeBool IsA(const char* type) override { return this->thisClass::IsTypeOf(type); } \static thisClass* SafeDownCast(vtkObjectBase* o) \{ \if (o && o->IsA(thisClassName)) \{ \return static_cast<thisClass*>(o); \} \return nullptr; \} \VTK_NEWINSTANCE instanceType* NewInstance() const \{ \return instanceType::SafeDownCast(this->NewInstanceInternal()); \} \static vtkIdType GetNumberOfGenerationsFromBaseType(const char* type) \{ \if (!strcmp(thisClassName, type)) \{ \return 0; \} \return 1 + superclass::GetNumberOfGenerationsFromBaseType(type); \} \vtkIdType GetNumberOfGenerationsFromBase(const char* type) override \{ \return this->thisClass::GetNumberOfGenerationsFromBaseType(type); \}
1.2 vtkObjectFactory
vtkObjectFactory定义了对象实例化的接口,同时将所有的对象工厂存储到静态成员变量RegisteredFactories。
二、关键流程
2.1 类型注册
在VTK中,CMake/vtkObjectFactory.cmake 定义了 vtk_object_factory_configure用于自动生成对应的vtkObjectFactory,
// CMake/vtkObjectFactory.cxx.in@_vtk_object_factory_configure_EXPORT_MACRO@ void @_vtk_object_factory_library_name@_AutoInit_Construct()
{if(++@_vtk_object_factory_library_name@Count == 1){
@_vtk_object_factory_configure_INITIAL_CODE@@_vtk_object_factory_library_name@ObjectFactory* factory = @_vtk_object_factory_library_name@ObjectFactory::New();if (factory){// vtkObjectFactory keeps a reference to the "factory",vtkObjectFactory::RegisterFactory(factory);factory->Delete();}}
}
然后,借助于VTK_MODULE_INIT宏完成vtkObjectFactory的注册。
// Common/Core/vtkAutoInit.h// Description:
// Initialize the named module, ensuring its object factory is correctly
// registered. This call must be made in global scope in the
// translation unit of your executable (which can include a shared library, but
// will not work as expected in a static library).
//
// @code{.cpp}
// #include "vtkAutoInit.h"
// VTK_MODULE_INIT(vtkRenderingOpenGL2);
// @endcode
//
// The above snippet if included in the global scope will ensure the object
// factories for vtkRenderingOpenGL2 are correctly registered and unregistered.
#define VTK_MODULE_INIT(M) \VTK_AUTOINIT_DECLARE(M) namespace \{ \static struct M##_ModuleInit \{ \/* Call <mod>_AutoInit_Construct during initialization. */ \M##_ModuleInit() { VTK_AUTOINIT_CONSTRUCT(M) } \} M##_ModuleInit_Instance; \}
2.2 对象创建
vtkObject* vtkObjectFactory::CreateInstance(const char* vtkclassname, bool)
{if (!vtkObjectFactory::RegisteredFactories){vtkObjectFactory::Init();}vtkObjectFactory* factory;vtkCollectionSimpleIterator osit;for (vtkObjectFactory::RegisteredFactories->InitTraversal(osit);(factory = vtkObjectFactory::RegisteredFactories->GetNextObjectFactory(osit));){vtkObject* newobject = factory->CreateObject(vtkclassname);if (newobject){return newobject;}}return nullptr;
}
三、总结
由上述对VTK类型系统的分析,同时结合<FreeCAD源码分析:Type System>一文,可以总结出类型系统实现的要点,
- 定义对象基类
- 定义构造器及其接口
- 将构造器(手动/自动)注册到类型注册表
- 根据类型名字获取构造器,然后实例化对象
VTK | FreeCAD | |
基类 | vtkObjectBase | Base::BaseClass |
构造器 | vtkObjectFactory | Base::Type |
类型注册表 | vtkObjectFactory::RegisteredFactories | Base::Type::typedata |
参考文献
- Erich Gamma. Design Patterns:elements of reusable object-oriented software. Addison Wesley, 1994.
网络资料
VTKhttps://vtk.org/
大型CAx(CAD/CAE/CAM)软件研发中的职责编排https://blog.csdn.net/qq_26221775/article/details/136975550?spm=1001.2014.3001.5502
FreeCAD源码分析:Type Systemhttps://blog.csdn.net/qq_26221775/article/details/140468677?spm=1001.2014.3001.5502