KiCad 插件系统

本文档由以下列出的贡献者版权所有(C)2016。 您可以根据GNU通用公共许可证(http://www.gnu.org/licenses/gpl.html),版本3或更高版本或Creative Commons Attribution License(http)的条款进行分发和/或修改。 ://creativecommons.org/licenses/by/3.0/),3.0或更高版本。

本指南中的所有商标均属于其合法所有者。

贡献者

Cirilo Bernardo

翻译

taotieren <admin@taotieren.com>, 2019

Telegram 简体中文交流群: https://t.me/KiCad_zh_CN

反馈

请将任何错误报告、建议或新版本引导到此处:

出版日期和软件版本

2014年1月26日发布。

KiCad 插件系统简介

KiCad 插件系统是一个使用共享库扩展 KiCad 功能的框架。 使用插件的一个主要优点是在开发插件时没有必要重建 KiCad 套件; 事实上,可以借助 KiCad 源代码树中的一小组标题构建插件。 通过确保开发人员仅编译与正在开发的插件直接相关的代码,从而减少每个构建和测试周期所需的时间,在插件开发期间删除构建 KiCad 的要求极大地提高了工作效率。

插件最初是为 3D 模型查看器开发的,因此可以支持更多类型的 3D 模型,而无需对支持的每种新模型类型的 KiCad 源进行重大更改。 插件框架后来被推广,以便将来开发人员可以创建不同类型的插件。 目前,只有 3D 插件在 KiCad 中实现,但可以想象最终将开发 PCB 插件,以使用户能够实现数据导入器和导出器。

插件类

插件分为插件类,因为每个插件都解决了特定域中的问题,因此需要该域独有的接口。 例如,3D 模型插件从文件加载 3D 模型数据并将该数据转换为可由 3D 查看器显示的格式。 PCB 导入/导出插件将获取 PCB 数据并导出为其他电气或机械数据格式,或将外部格式转换为 KiCad PCB。 目前只开发了 3D 插件类,它将成为本文档的重点。

实现插件类需要在 KiCad 源代码树中创建代码来管理插件代码的加载。在 KiCad 源代码树中,文件‘plugins/ldr/pluginldr.h’声明了所有插件加载器的基类。这个类声明了我们期望在任何 KiCad 插件(样板代码)中找到的最基本的函数,它的实现提供了对插件加载器和可用插件之间的版本兼容性的基本检查。标题‘plugins/ldr/3d/pluginldr3D.h’声明了 3D 插件类的加载器。加载器负责加载给定的插件并使其功能可用于 KiCad。插件加载器的每个实例代表一个实际的插件实现,并充当 KiCad 和插件功能之间的透明桥梁。加载器不是 KiCad 中支持插件所需的唯一代码:我们还需要代码来发现插件和代码,以通过插件加载器调用插件的功能。在 3D 插件的情况下,发现和调用功能都包含在 S3D_CACHE 类中。

除非正在开发新的插件类,否则插件开发人员不需要关心 KiCad 管理插件的内部代码的细节; 插件只需要定义其特定插件类声明的函数。

标题‘include/plugins/kicad_plugin.h’声明了所有 KiCad 插件所需的泛型函数; 这些函数标识插件类,提供特定插件的名称,提供插件类 API 的版本信息,提供特定插件的版本信息,并提供插件类 API 的基本版本兼容性检查。 简而言之,这些功能是:

/* Return a UTF-8 string naming the Plugin Class */
char const* GetKicadPluginClass( void );

/* Return version information for the Plugin Class API */
void GetClassVersion( unsigned char* Major, unsigned char* Minor,
     unsigned char* Patch, unsigned char* Revision );

/*
   Return true if the version check implemented in the plugin
   determines that the given Plugin Class API is compatible.
 */
bool CheckClassVersion( unsigned char Major,
    unsigned char Minor, unsigned char Patch, unsigned char Revision );

/* Return the name of the specific plugin, for example "PLUGIN_3D_VRML" */
const char* GetKicadPluginName( void );

/* Return version information for the specific plugin */
void GetPluginVersion( unsigned char* Major, unsigned char* Minor,
     unsigned char* Patch, unsigned char* Revision );

插件类:PLUGIN_3D

标题‘include/plugins/3d/3d_plugin.h’声明了必须由所有 3D 插件实现的函数,并定义了插件所需的许多函数以及用户不得重新实现的函数。 用户不得重新实现的已定义函数是:

/* Returns the Plugin Class name "PLUGIN_3D" */
char const* GetKicadPluginClass( void );

/* Return version information for the PLUGIN_3D API */
void GetClassVersion( unsigned char* Major, unsigned char* Minor,
     unsigned char* Patch, unsigned char* Revision );

/*
   Performs basic version checks enforced by the developers of
   the loader for the PLUGIN_3D class and returns true if the
   checks pass
 */
bool CheckClassVersion( unsigned char Major, unsigned char Minor,
     unsigned char Patch, unsigned char Revision );

用户必须实现的功能如下:

/* Return the number of extension strings supported by the plugin */
int GetNExtensions( void );

/*
   Return the requested extension string; valid values are 0 to
   GetNExtensions() - 1
 */
char const* GetModelExtension( int aIndex );

/* Return the total number of file filters supported by the plugin */
int GetNFilters( void );

/*
   Return the file filter requested; valid values are 0 to
   GetNFilters() - 1
 */
char const* GetFileFilter( int aIndex );

/*
    Return true if the plugin can render this type of 3D model.
    In some cases a plugin may not yet provide a visual model
    and must return false.
 */
bool CanRender( void );

/* Load the specified model and return a pointer to its visual model data */
SCENEGRAPH* Load( char const* aFileName );

教程:3D插件类

本节包含对 PLUGIN_3D 类的两个非常简单的插件的描述,并引导用户完成代码的设置和构建。

基本的 3D 插件

本教程将引导用户开发一个名为“PLUGIN_3D_DEMO1”的非常基本的 3D 插件。 本教程的目的只是为了演示一个非常基本的 3D 插件的构造,除了提供一些允许 KiCad 用户在浏览 3D 模型时过滤文件名的过滤字符串之外什么都不做。 这里演示的代码是任何 3D 插件的绝对最低要求,可以用作创建更高级插件的模板。

为了构建演示项目,我们需要以下内容:

  • CMake

  • KiCad 插件标头

  • KiCad 场景图库‘kicad_3dsg’

要自动检测 KiCad 标头和库,我们将使用 CMake FindPackage 脚本; 如果相关的头文件安装到‘${KICAD_ROOT_DIR}/kicad’并且 KiCad Scene Graph 库安装在‘${KICAD_ROOT_DIR}/lib’中,则本教程中提供的脚本应该适用于 Linux 和 Windows。

要开始,让我们创建一个项目目录和 FindPackage 脚本:

mkdir demo && cd demo
export DEMO_ROOT=${PWD}
mkdir CMakeModules && cd CMakeModules
cat > FindKICAD.cmake << _EOF
find_path( KICAD_INCLUDE_DIR kicad/plugins/kicad_plugin.h
    PATHS ${KICAD_ROOT_DIR}/include $ENV{KICAD_ROOT_DIR}/include
    DOC "Kicad plugins header path."
    )

if( NOT ${KICAD_INCLUDE_DIR} STREQUAL "KICAD_INCLUDE_DIR-NOTFOUND" )

    # attempt to extract the version information from sg_version.h
    find_file( KICAD_SGVERSION sg_version.h
        PATHS ${KICAD_INCLUDE_DIR}
        PATH_SUFFIXES kicad/plugins/3dapi
        NO_DEFAULT_PATH )

    if( NOT ${KICAD_SGVERSION} STREQUAL "KICAD_SGVERSION-NOTFOUND" )

        # extract the "#define KICADSG_VERSION*" lines
        file( STRINGS ${KICAD_SGVERSION} _version REGEX "^#define.*KICADSG_VERSION.*" )

        foreach( SVAR ${_version} )
            string( REGEX MATCH KICADSG_VERSION_[M,A,J,O,R,I,N,P,T,C,H,E,V,I,S]* _VARNAME ${SVAR} )
            string( REGEX MATCH [0-9]+ _VALUE ${SVAR} )

            if( NOT ${_VARNAME} STREQUAL "" AND NOT ${_VALUE} STREQUAL "" )
                set( _${_VARNAME} ${_VALUE} )
            endif()

        endforeach()

        #ensure that NOT SG3D_VERSION* will evaluate to '0'
        if( NOT _KICADSG_VERSION_MAJOR )
            set( _KICADSG_VERSION_MAJOR 0 )
        endif()

        if( NOT _KICADSG_VERSION_MINOR )
            set( _KICADSG_VERSION_MINOR 0 )
        endif()

        if( NOT _KICADSG_VERSION_PATCH )
            set( _KICADSG_VERSION_PATCH 0 )
        endif()

        if( NOT _KICADSG_VERSION_REVISION )
            set( _KICADSG_VERSION_REVISION 0 )
        endif()

        set( KICAD_VERSION ${_KICADSG_VERSION_MAJOR}.${_KICADSG_VERSION_MINOR}.${_KICADSG_VERSION_PATCH}.${_KICADSG_VERSION_REVISION} )
        unset( KICAD_SGVERSION CACHE )

    endif()
endif()


find_library( KICAD_LIBRARY
    NAMES kicad_3dsg
    PATHS
        ${KICAD_ROOT_DIR}/lib $ENV{KICAD_ROOT_DIR}/lib
        ${KICAD_ROOT_DIR}/bin $ENV{KICAD_ROOT_DIR}/bin
    DOC "Kicad scenegraph library path."
    )

include( FindPackageHandleStandardArgs )
FIND_PACKAGE_HANDLE_STANDARD_ARGS( KICAD
    REQUIRED_VARS
        KICAD_INCLUDE_DIR
        KICAD_LIBRARY
        KICAD_VERSION
    VERSION_VAR KICAD_VERSION )


mark_as_advanced( KICAD_INCLUDE_DIR )
set( KICAD_VERSION_MAJOR ${_KICADSG_VERSION_MAJOR} CACHE INTERNAL "" )
set( KICAD_VERSION_MINOR ${_KICADSG_VERSION_MINOR} CACHE INTERNAL "" )
set( KICAD_VERSION_PATCH ${_KICADSG_VERSION_PATCH} CACHE INTERNAL "" )
set( KICAD_VERSION_TWEAK ${_KICADSG_VERSION_REVISION} CACHE INTERNAL "" )
_EOF

必须安装 Kicad 及其插件标头; 如果将它们安装到用户目录或 Linux 上的‘/opt’下,或者您使用的是 Windows,则需要将‘KICAD_ROOT_DIR’环境变量设置为指向包含 KiCad‘include’和‘lib’目录的目录。 对于 OS X,此处显示的 FindPackage 脚本可能需要进行一些调整。

要配置和构建教程代码,我们将使用 CMake 并创建一个‘CMakeLists.txt’脚本文件:

cd ${DEMO_ROOT}
cat > CMakeLists.txt << _EOF
# declare the name of the project
project( PLUGIN_DEMO )

# check that we have a version of CMake with all required features
cmake_minimum_required( VERSION 2.8.12 FATAL_ERROR )

# inform CMake of where to find the FindKICAD script
set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules )

# attempt to discover the installed kicad headers and library
# and set the variables:
#     KICAD_INCLUDE_DIR
#     KICAD_LIBRARY
find_package( KICAD 1.0 REQUIRED )

# add the kicad include directory to the compiler's search path
include_directories( ${KICAD_INCLUDE_DIR}/kicad )

# create a plugin named s3d_plugin_demo1
add_library( s3d_plugin_demo1 MODULE
    src/s3d_plugin_demo1.cpp
    )

_EOF

第一个演示项目非常基础; 它由一个文件组成,除了编译器默认值之外没有外部链接依赖项。 我们首先创建一个源目录:

cd ${DEMO_ROOT}
mkdir src && cd src
export DEMO_SRC=${PWD}

现在我们自己创建插件源:

s3d_plugin_demo1.cpp
#include <iostream>

// the 3d_plugin.h header defines the functions required of 3D plugins
#include "plugins/3d/3d_plugin.h"

// define the version information of this plugin; do not confuse this
// with the Plugin Class version which is defined in 3d_plugin.h
#define PLUGIN_3D_DEMO1_MAJOR 1
#define PLUGIN_3D_DEMO1_MINOR 0
#define PLUGIN_3D_DEMO1_PATCH 0
#define PLUGIN_3D_DEMO1_REVNO 0

// implement the function which provides users with this plugin's name
const char* GetKicadPluginName( void )
{
    return "PLUGIN_3D_DEMO1";
}

// implement the function which provides users with this plugin's version
void GetPluginVersion( unsigned char* Major, unsigned char* Minor,
    unsigned char* Patch, unsigned char* Revision )
{
    if( Major )
        *Major = PLUGIN_3D_DEMO1_MAJOR;

    if( Minor )
        *Minor = PLUGIN_3D_DEMO1_MINOR;

    if( Patch )
        *Patch = PLUGIN_3D_DEMO1_PATCH;

    if( Revision )
        *Revision = PLUGIN_3D_DEMO1_REVNO;

    return;
}

// number of extensions supported; on *NIX systems the extensions are
// provided twice - once in lower case and once in upper case letters
#ifdef _WIN32
    #define NEXTS 7
#else
    #define NEXTS 14
#endif

// number of filter sets supported
#define NFILS 5

// define the extension strings and filter strings which this
// plugin will supply to the user
static char ext0[] = "wrl";
static char ext1[] = "x3d";
static char ext2[] = "emn";
static char ext3[] = "iges";
static char ext4[] = "igs";
static char ext5[] = "stp";
static char ext6[] = "step";

#ifdef _WIN32
static char fil0[] = "VRML 1.0/2.0 (*.wrl)|*.wrl";
static char fil1[] = "X3D (*.x3d)|*.x3d";
static char fil2[] = "IDF 2.0/3.0 (*.emn)|*.emn";
static char fil3[] = "IGESv5.3 (*.igs;*.iges)|*.igs;*.iges";
static char fil4[] = "STEP (*.stp;*.step)|*.stp;*.step";
#else
static char ext7[] = "WRL";
static char ext8[] = "X3D";
static char ext9[] = "EMN";
static char ext10[] = "IGES";
static char ext11[] = "IGS";
static char ext12[] = "STP";
static char ext13[] = "STEP";

static char fil0[] = "VRML 1.0/2.0 (*.wrl;*.WRL)|*.wrl;*.WRL";
static char fil1[] = "X3D (*.x3d;*.X3D)|*.x3d;*.X3D";
static char fil2[] = "IDF 2.0/3.0 (*.emn;*.EMN)|*.emn;*.EMN";
static char fil3[] = "IGESv5.3 (*.igs;*.iges;*.IGS;*.IGES)|*.igs;*.iges;*.IGS;*.IGES";
static char fil4[] = "STEP (*.stp;*.step;*.STP;*.STEP)|*.stp;*.step;*.STP;*.STEP";
#endif

// instantiate a convenient data structure for accessing the
// lists of extension and filter strings
static struct FILE_DATA
{
    char const* extensions[NEXTS];
    char const* filters[NFILS];

    FILE_DATA()
    {
        extensions[0] = ext0;
        extensions[1] = ext1;
        extensions[2] = ext2;
        extensions[3] = ext3;
        extensions[4] = ext4;
        extensions[5] = ext5;
        extensions[6] = ext6;
        filters[0] = fil0;
        filters[1] = fil1;
        filters[2] = fil2;
        filters[3] = fil3;
        filters[4] = fil4;

#ifndef _WIN32
        extensions[7] = ext7;
        extensions[8] = ext8;
        extensions[9] = ext9;
        extensions[10] = ext10;
        extensions[11] = ext11;
        extensions[12] = ext12;
        extensions[13] = ext13;
#endif
        return;
    }

} file_data;


// return the number of extensions supported by this plugin
int GetNExtensions( void )
{
    return NEXTS;
}

// return the indexed extension string
char const* GetModelExtension( int aIndex )
{
    if( aIndex < 0 || aIndex >= NEXTS )
        return NULL;

    return file_data.extensions[aIndex];
}

// return the number of filter strings provided by this plugin
int GetNFilters( void )
{
    return NFILS;
}

// return the indexed filter string
char const* GetFileFilter( int aIndex )
{
    if( aIndex < 0 || aIndex >= NFILS )
        return NULL;

    return file_data.filters[aIndex];
}

// return false since this plugin does not provide visualization data
bool CanRender( void )
{
    return false;
}

// return NULL since this plugin does not provide visualization data
SCENEGRAPH* Load( char const* aFileName )
{
    // this dummy plugin does not support rendering of any models
    return NULL;
}

此源文件满足实现 3D 插件的所有最低要求。 该插件不会为渲染模型生成任何数据,但它可以为 KiCad 提供支持的模型文件扩展名和文件扩展名过滤器列表,以增强 3D 模型文件选择对话框。 在 KiCad 中,扩展字符串用于选择可用于加载指定模型的插件; 例如,如果插件是‘wrl’,那么 KiCad 将调用声称支持扩展‘wrl’的每个插件,直到插件返回可视化数据。 每个插件提供的文件过滤器将传递到 3D 文件选择器对话框,以改善浏览 UI。

要构建插件:

cd ${DEMO_ROOT}
# export KICAD_ROOT_DIR if necessary
mkdir build && cd build
cmake .. && make

该插件将被构建但未安装; 如果要加载插件,必须将插件文件复制到 KiCad 的插件目录。

高级 3D 插件

本教程将引导用户开发名为“PLUGIN_3D_DEMO2”的 3D 插件。 本教程的目的是演示 KiCad 预览器可以渲染的非常基本的场景图的构造。 该插件声称处理‘txt’类型的文件。 虽然文件必须存在,以便缓存管理器调用插件,但此插件不处理文件内容; 相反,插件只是创建一个包含一对四面体的场景图。 本教程假定第一个教程已完成,并且已创建 CMakeLists.txt 和 FindKICAD.cmake 脚本文件。

将新的源文件放在与上一个教程的源文件相同的目录中,我们将扩展上一个教程的 CMakeLists.txt 文件来构建本教程。 由于这个插件会为 KiCad 创建一个场景图,我们需要链接到 KiCad 的场景图库‘kicad_3dsg’。 KiCad 的场景图库提供了一组可用于构建场景图对象的类; 场景图对象是 3D 缓存管理器使用的中间数据可视化格式。 所有支持模型可视化的插件都必须通过此库将模型数据转换为场景图。

第一步是扩展‘CMakeLists.txt’来构建这个教程项目:

cd ${DEMO_ROOT}
cat >> CMakeLists.txt << _EOF
add_library( s3d_plugin_demo2 MODULE
    src/s3d_plugin_demo2.cpp
    )

target_link_libraries( s3d_plugin_demo2 ${KICAD_LIBRARY} )
_EOF

现在我们切换到源目录并创建源文件:

cd ${DEMO_SRC}
s3d_plugin_demo2.cpp
#include <cmath>
// 3D Plugin Class declarations
#include "plugins/3d/3d_plugin.h"
// interface to KiCad Scene Graph Library
#include "plugins/3dapi/ifsg_all.h"

// version information for this plugin
#define PLUGIN_3D_DEMO2_MAJOR 1
#define PLUGIN_3D_DEMO2_MINOR 0
#define PLUGIN_3D_DEMO2_PATCH 0
#define PLUGIN_3D_DEMO2_REVNO 0

// provide the name of this plugin
const char* GetKicadPluginName( void )
{
    return "PLUGIN_3D_DEMO2";
}

// provide the version of this plugin
void GetPluginVersion( unsigned char* Major, unsigned char* Minor,
    unsigned char* Patch, unsigned char* Revision )
{
    if( Major )
        *Major = PLUGIN_3D_DEMO2_MAJOR;

    if( Minor )
        *Minor = PLUGIN_3D_DEMO2_MINOR;

    if( Patch )
        *Patch = PLUGIN_3D_DEMO2_PATCH;

    if( Revision )
        *Revision = PLUGIN_3D_DEMO2_REVNO;

    return;
}


// number of extensions supported
#ifdef _WIN32
#define NEXTS 1
#else
#define NEXTS 2
#endif

// number of filter sets supported
#define NFILS 1

static char ext0[] = "txt";

#ifdef _WIN32
static char fil0[] = "demo (*.txt)|*.txt";
#else
static char ext1[] = "TXT";

static char fil0[] = "demo (*.txt;*.TXT)|*.txt;*.TXT";
#endif


static struct FILE_DATA
{
    char const* extensions[NEXTS];
    char const* filters[NFILS];

    FILE_DATA()
    {
        extensions[0] = ext0;
        filters[0] = fil0;

#ifndef _WIN32
        extensions[1] = ext1;
#endif
        return;
    }

} file_data;


int GetNExtensions( void )
{
    return NEXTS;
}


char const* GetModelExtension( int aIndex )
{
    if( aIndex < 0 || aIndex >= NEXTS )
        return NULL;

    return file_data.extensions[aIndex];
}


int GetNFilters( void )
{
    return NFILS;
}


char const* GetFileFilter( int aIndex )
{
    if( aIndex < 0 || aIndex >= NFILS )
        return NULL;

    return file_data.filters[aIndex];
}


// return true since this plugin can provide visualization data
bool CanRender( void )
{
    return true;
}


// create the visualization data
SCENEGRAPH* Load( char const* aFileName )
{
    // For this demonstration we create a tetrahedron (tx1) consisting
    // of a SCENEGRAPH (VRML Transform) which in turn contains 4
    // SGSHAPE (VRML Shape) objects representing each of the sides of
    // the tetrahedron. Each Shape is associated with a color (SGAPPEARANCE)
    // and a SGFACESET (VRML Geometry->indexedFaceSet). Each SGFACESET is
    // associated with a vertex list (SGCOORDS), a per-vertex normals
    // list (SGNORMALS), and a coordinate index (SGCOORDINDEX). One shape
    // is used to represent each face so that we may use per-vertex-per-face
    // normals.
    //
    // The tetrahedron in turn is a child of a top level SCENEGRAPH (tx0)
    // which has a second SCENEGRAPH child (tx2) which is a transformation
    // of the tetrahedron tx1 (rotation + translation). This demonstrates
    // the reuse of components within the scene graph hierarchy.

    // define the vertices of the tetrahedron
    // face 1: 0, 3, 1
    // face 2: 0, 2, 3
    // face 3: 1, 3, 2
    // face 4: 0, 1, 2
    double SQ2 = sqrt( 0.5 );
    SGPOINT vert[4];
    vert[0] = SGPOINT( 1.0, 0.0, -SQ2 );
    vert[1] = SGPOINT( -1.0, 0.0, -SQ2 );
    vert[2] = SGPOINT( 0.0, 1.0, SQ2 );
    vert[3] = SGPOINT( 0.0, -1.0, SQ2 );


    // create the top level transform; this will hold all other
    // scenegraph objects; a transform may hold other transforms and
    // shapes
    IFSG_TRANSFORM* tx0 = new IFSG_TRANSFORM( true );

    // create the transform which will house the shapes
    IFSG_TRANSFORM* tx1 = new IFSG_TRANSFORM( tx0->GetRawPtr() );

    // add a shape which we will use to define one face of the tetrahedron;
    // shapes hold facesets and appearances
    IFSG_SHAPE* shape = new IFSG_SHAPE( *tx1 );

    // add a faceset; these contain coordinate lists, coordinate indices,
    // vertex lists, vertex indices, and may also contain color lists and
    // their indices.

    IFSG_FACESET* face = new IFSG_FACESET( *shape );

    IFSG_COORDS* cp = new IFSG_COORDS( *face );
    cp->AddCoord( vert[0] );
    cp->AddCoord( vert[3] );
    cp->AddCoord( vert[1] );

    // coordinate indices - note: enforce triangles;
    // in real plugins where it is not necessarily possible
    // to determine which side a triangle is visible from,
    // 2 point orders must be specified for each triangle
    IFSG_COORDINDEX* coordIdx = new IFSG_COORDINDEX( *face );
    coordIdx->AddIndex( 0 );
    coordIdx->AddIndex( 1 );
    coordIdx->AddIndex( 2 );

    // create an appearance; appearances are owned by shapes

    // magenta
    IFSG_APPEARANCE* material = new IFSG_APPEARANCE( *shape);
    material->SetSpecular( 0.1, 0.0, 0.1 );
    material->SetDiffuse( 0.8, 0.0, 0.8 );
    material->SetAmbient( 0.2, 0.2, 0.2 );
    material->SetShininess( 0.2 );

    // normals
    IFSG_NORMALS* np = new IFSG_NORMALS( *face );
    SGVECTOR nval = S3D::CalcTriNorm( vert[0], vert[3], vert[1] );
    np->AddNormal( nval );
    np->AddNormal( nval );
    np->AddNormal( nval );

    //
    // Shape2
    // Note: we reuse the IFSG* wrappers to create and manipulate new
    // data structures.
    //
    shape->NewNode( *tx1 );
    face->NewNode( *shape );
    coordIdx->NewNode( *face );
    cp->NewNode( *face );
    np->NewNode( *face );

    // vertices
    cp->AddCoord( vert[0] );
    cp->AddCoord( vert[2] );
    cp->AddCoord( vert[3] );

    // indices
    coordIdx->AddIndex( 0 );
    coordIdx->AddIndex( 1 );
    coordIdx->AddIndex( 2 );

    // normals
    nval = S3D::CalcTriNorm( vert[0], vert[2], vert[3] );
    np->AddNormal( nval );
    np->AddNormal( nval );
    np->AddNormal( nval );
    // color (red)
    material->NewNode( *shape );
    material->SetSpecular( 0.2, 0.0, 0.0 );
    material->SetDiffuse( 0.9, 0.0, 0.0 );
    material->SetAmbient( 0.2, 0.2, 0.2 );
    material->SetShininess( 0.1 );

    //
    // Shape3
    //
    shape->NewNode( *tx1 );
    face->NewNode( *shape );
    coordIdx->NewNode( *face );
    cp->NewNode( *face );
    np->NewNode( *face );

    // vertices
    cp->AddCoord( vert[1] );
    cp->AddCoord( vert[3] );
    cp->AddCoord( vert[2] );

    // indices
    coordIdx->AddIndex( 0 );
    coordIdx->AddIndex( 1 );
    coordIdx->AddIndex( 2 );

    // normals
    nval = S3D::CalcTriNorm( vert[1], vert[3], vert[2] );
    np->AddNormal( nval );
    np->AddNormal( nval );
    np->AddNormal( nval );

    // color (green)
    material->NewNode( *shape );
    material->SetSpecular( 0.0, 0.1, 0.0 );
    material->SetDiffuse( 0.0, 0.9, 0.0 );
    material->SetAmbient( 0.2, 0.2, 0.2 );
    material->SetShininess( 0.1 );

    //
    // Shape4
    //
    shape->NewNode( *tx1 );
    face->NewNode( *shape );
    coordIdx->NewNode( *face );
    cp->NewNode( *face );
    np->NewNode( *face );

    // vertices
    cp->AddCoord( vert[0] );
    cp->AddCoord( vert[1] );
    cp->AddCoord( vert[2] );

    // indices
    coordIdx->AddIndex( 0 );
    coordIdx->AddIndex( 1 );
    coordIdx->AddIndex( 2 );

    // normals
    nval = S3D::CalcTriNorm( vert[0], vert[1], vert[2] );
    np->AddNormal( nval );
    np->AddNormal( nval );
    np->AddNormal( nval );

    // color (blue)
    material->NewNode( *shape );
    material->SetSpecular( 0.0, 0.0, 0.1 );
    material->SetDiffuse( 0.0, 0.0, 0.9 );
    material->SetAmbient( 0.2, 0.2, 0.2 );
    material->SetShininess( 0.1 );

    // create a copy of the entire tetrahedron shifted Z+2 and rotated 2/3PI
    IFSG_TRANSFORM* tx2 = new IFSG_TRANSFORM( tx0->GetRawPtr() );
    tx2->AddRefNode( *tx1 );
    tx2->SetTranslation( SGPOINT( 0, 0, 2 ) );
    tx2->SetRotation( SGVECTOR( 0, 0, 1 ), M_PI*2.0/3.0 );

    SGNODE* data = tx0->GetRawPtr();

    // delete the wrappers
    delete shape;
    delete face;
    delete coordIdx;
    delete material;
    delete cp;
    delete np;
    delete tx0;
    delete tx1;
    delete tx2;

    return (SCENEGRAPH*)data;
}

应用程序编程接口(API)

插件通过应用程序编程接口(API)实现实现。 每个插件类都有其特定的 API,在 3D 插件教程中,我们已经看到了由标题“3d_plugin.h” 声明的 3D 插件 API 实现的示例。 插件也可能依赖于 KiCad 源代码树中定义的其他 API; 在 3D 插件的情况下,支持模型可视化的所有插件必须与标题‘ifsg_all.h’ 及其包含的标题中声明的 Scene Graph API 交互。

本节描述了插件类实现可能需要的插件类 API 和其他 KiCad API 的详细信息。

插件类 API

目前只有一个为 KiCad 声明的插件类:3D 插件类。 所有 KiCad 插件类都必须实现头文件‘kicad_plugin.h’中声明的一组基本函数; 这些声明称为 Base Kicad 插件类。 不存在 Base Kicad 插件类的实现; 头文件的存在纯粹是为了确保插件开发人员在每个插件实现中实现这些定义的函数。

在 KiCad 中,插件加载器的每个实例都实现了插件提供的 API,就像插件加载器是提供插件服务的类一样。 这是通过 Plugin Loader 类实现的,该类提供包含与插件实现的类似的函数名的公共接口; 如果例如没有加载插件,则参数列表可以变化以适应向用户通知可能遇到的任何问题的需要。 在内部,插件加载器使用存储的指针指向每个 API 函数,以代表用户调用每个函数。

API:Base Kicad 插件类

Base Kicad 插件类由头文件‘kicad_plugin.h’ 定义。 此标头必须包含在所有其他插件类的声明中; 例如,请参阅头文件‘3d_plugin.h’ 中的 3D 插件类声明。 这些函数的原型在 《 plugin-classes,Plugin Classes 》 中简要描述。 API 由“pluginldr.cpp” 中定义的基本插件加载器实现。

为了帮助理解基本 KiCad 插件头所需的功能,我们必须查看基本插件 Loader 类中发生的情况。 Plugin Loader 类声明了一个虚函数‘Open()’,它接受要加载的插件的完整路径。 在特定的插件类加载器中实现 ‘Open()’ 函数最初将调用基本插件加载器的受保护的‘open()’ 函数; 这个基础‘open()’ 函数试图找到每个必需的基本插件函数的地址; 一旦检索到每个函数的地址,就会强制执行一些检查:

  1. 调用插件‘GetKicadPluginClass()’,并将结果与插件加载器实现提供的插件类字符串进行比较; 如果这些字符串不匹配,则打开的插件不适用于 Plugin Loader 实例。

  2. 调用插件‘GetClassVersion()’来检索插件实现的插件类 API 版本。

  3. 插件加载器虚拟‘GetLoaderVersion()’函数被调用以检索由加载器实现的插件类 API 版本。

  4. 插件和加载器报告的插件类 API 版本必须具有相同的主版本号,否则它们被认为是不兼容的。 这是最基本的版本测试,它由基本插件加载器强制执行。

  5. 使用插件加载器的插件类 API 版本信息调用插件‘CheckClassVersion()’; 如果插件支持给定版本,则返回“true” 表示成功。 如果成功,加载器根据‘GetKicadPluginName()’和‘GetPluginVersion()’的结果创建一个 PluginInfo 字符串,插件加载过程在 Plugin Loader 的‘Open()’实现中继续。

API:3D 插件类

3D 插件类由头文件‘3d_plugin.h’声明,它扩展了所需的插件函数,如《class-plugin-3d,Plugin Class:PLUGIN_3D 》中所述。 相应的插件加载器在‘pluginldr3D.cpp’中定义,除了所需的API函数之外,加载器还实现了以下公共函数:

/* Open the plugin specified by the full path "aFullFileName" */
bool Open( const wxString& aFullFileName );

/* Close the currently opened plugin */
void Close( void );

/* Retrieve the Plugin Class API Version implemented by this Plugin Loader */
void GetLoaderVersion( unsigned char* Major, unsigned char* Minor,
    unsigned char* Revision, unsigned char* Patch ) const;

所需的 3D 插件类功能通过以下功能公开:

/* returns the Plugin Class or NULL if no plugin loaded */
char const* GetKicadPluginClass( void );

/* returns false if no plugin loaded */
bool GetClassVersion( unsigned char* Major, unsigned char* Minor,
    unsigned char* Patch, unsigned char* Revision );

/* returns false if the class version check fails or no plugin is loaded */
bool CheckClassVersion( unsigned char Major, unsigned char Minor,
    unsigned char Patch, unsigned char Revision );

/* returns the Plugin Name or NULL if no plugin loaded */
const char* GetKicadPluginName( void );

/*
   returns false if no plugin is loaded, otherwise the arguments
   contain the result of GetPluginVersion()
 */
bool GetVersion( unsigned char* Major, unsigned char* Minor,
    unsigned char* Patch, unsigned char* Revision );

/*
   sets aPluginInfo to an empty string if no plugin is loaded,
   otherwise aPluginInfo is set to a string of the form:
   [NAME]:[MAJOR].[MINOR].[PATCH].[REVISION] where
   NAME = name provided by GetKicadPluginClass()
   MAJOR, MINOR, PATCH, REVISION = version information from
   GetPluginVersion()
 */
void GetPluginInfo( std::string& aPluginInfo );

在典型情况下,用户将执行以下操作:

  1. 创建‘KICAD_PLUGIN_LDR_3D’的实例。

  2. 调用‘Open(“/path/to/myplugin.so”)’来打开一个特定的插件。 必须检查返回值以确保根据需要加载插件。

  3. 调用由‘KICAD_PLUGIN_LDR_3D’公开的任何 3D 插件类调用。

  4. 调用‘Close()’来关闭(取消链接)插件。

  5. 销毁‘KICAD_PLUGIN_LDR_3D’实例。

场景图类 API

Scenegraph 类 API 由标题‘ifsg_all.h’及其包含的标题定义。 API 由许多辅助例程组成,命名空间为‘S3D’,在‘ifsg_api.h’中定义,包装类由各种‘ifsg_*.h’标题定义; 包装器支持底层的场景图类,它们一起形成一个与 VRML2.0 静态场景图兼容的场景图结构。 标题,结构,类及其公共函数如下:

sg_version.h
/*
   Defines version information of the SceneGraph Classes.
   All plugins which use the scenegraph class should include this header
   and check the version information against the version reported by
   S3D::GetLibVersion() to ensure compatibility
 */

#define KICADSG_VERSION_MAJOR         2
#define KICADSG_VERSION_MINOR         0
#define KICADSG_VERSION_PATCH         0
#define KICADSG_VERSION_REVISION      0
sg_types.h
/*
   Defines the SceneGraph Class Types; these types
   are closely related to VRML2.0 node types.
 */

namespace S3D
{
    enum SGTYPES
    {
        SGTYPE_TRANSFORM = 0,
        SGTYPE_APPEARANCE,
        SGTYPE_COLORS,
        SGTYPE_COLORINDEX,
        SGTYPE_FACESET,
        SGTYPE_COORDS,
        SGTYPE_COORDINDEX,
        SGTYPE_NORMALS,
        SGTYPE_SHAPE,
        SGTYPE_END
    };
};

‘sg_base.h’头包含 scenegraph 类使用的基本数据类型的声明。

sg_base.h
/*
    This is an RGB color model equivalent to the VRML2.0
    RGB model where each color may have a value within the
    range [0..1].
 */

class SGCOLOR
{
public:
    SGCOLOR();
    SGCOLOR( float aRVal, float aGVal, float aBVal );

    void GetColor( float& aRedVal, float& aGreenVal, float& aBlueVal ) const;
    void GetColor( SGCOLOR& aColor ) const;
    void GetColor( SGCOLOR* aColor ) const;

    bool SetColor( float aRedVal, float aGreenVal, float aBlueVal );
    bool SetColor( const SGCOLOR& aColor );
    bool SetColor( const SGCOLOR* aColor );
};


class SGPOINT
{
public:
    double x;
    double y;
    double z;

public:
    SGPOINT();
    SGPOINT( double aXVal, double aYVal, double aZVal );

    void GetPoint( double& aXVal, double& aYVal, double& aZVal );
    void GetPoint( SGPOINT& aPoint );
    void GetPoint( SGPOINT* aPoint );

    void SetPoint( double aXVal, double aYVal, double aZVal );
    void SetPoint( const SGPOINT& aPoint );
};


/*
    A SGVECTOR has 3 components (x,y,z) similar to a point; however
    a vector ensures that the stored values are normalized and
    prevents direct manipulation of the component variables.
 */
class SGVECTOR
{
public:
    SGVECTOR();
    SGVECTOR( double aXVal, double aYVal, double aZVal );

    void GetVector( double& aXVal, double& aYVal, double& aZVal ) const;

    void SetVector( double aXVal, double aYVal, double aZVal );
    void SetVector( const SGVECTOR& aVector );

    SGVECTOR& operator=( const SGVECTOR& source );
};

‘IFSG_NODE’类是所有场景图节点的基类。 所有 scenegraph 对象都实现此类的公共函数,但在某些情况下,特定函数可能对特定类没有意义。

ifsg_node.h
class IFSG_NODE
{
public:
    IFSG_NODE();
    virtual ~IFSG_NODE();

    /**
     * Function Destroy
     * deletes the scenegraph object held by this wrapper
     */
    void Destroy( void );

    /**
     * Function Attach
     * associates a given SGNODE* with this wrapper
     */
    virtual bool Attach( SGNODE* aNode ) = 0;

    /**
     * Function NewNode
     * creates a new node to associate with this wrapper
     */
    virtual bool NewNode( SGNODE* aParent ) = 0;
    virtual bool NewNode( IFSG_NODE& aParent ) = 0;

    /**
     * Function GetRawPtr()
     * returns the raw internal SGNODE pointer
     */
    SGNODE* GetRawPtr( void );

    /**
     * Function GetNodeType
     * returns the type of this node instance
     */
    S3D::SGTYPES GetNodeType( void ) const;

    /**
     * Function GetParent
     * returns a pointer to the parent SGNODE of this object
     * or NULL if the object has no parent (ie. top level transform)
     * or if the wrapper is not currently associated with an SGNODE.
     */
    SGNODE* GetParent( void ) const;

    /**
     * Function SetParent
     * sets the parent SGNODE of this object.
     *
     * @param aParent [in] is the desired parent node
     * @return true if the operation succeeds; false if
     * the given node is not allowed to be a parent to
     * the derived object.
     */
    bool SetParent( SGNODE* aParent );

    /**
     * Function GetNodeTypeName
     * returns the text representation of the node type
     * or NULL if the node somehow has an invalid type
     */
    const char * GetNodeTypeName( S3D::SGTYPES aNodeType ) const;

    /**
     * Function AddRefNode
     * adds a reference to an existing node which is not owned by
     * (not a child of) this node.
     *
     * @return true on success
     */
    bool AddRefNode( SGNODE* aNode );
    bool AddRefNode( IFSG_NODE& aNode );

    /**
     * Function AddChildNode
     * adds a node as a child owned by this node.
     *
     * @return true on success
     */
    bool AddChildNode( SGNODE* aNode );
    bool AddChildNode( IFSG_NODE& aNode );
};

‘IFSG_TRANSFORM’类似于 VRML2.0 Transform 节点; 它可以包含任意数量的子 IFSG_SHAPE 和 IFSG_TRANSFORM 节点以及任意数量的引用的 IFSG_SHAPE 和 IFSG_TRANSFORM 节点。 有效的场景图必须有一个“IFSG_TRANSFORM” 对象作为根。

ifsg_transform.h
/**
 * Class IFSG_TRANSFORM
 * is the wrapper for the VRML compatible TRANSFORM block class SCENEGRAPH
 */

class IFSG_TRANSFORM : public IFSG_NODE
{
public:
    IFSG_TRANSFORM( bool create );
    IFSG_TRANSFORM( SGNODE* aParent );

    bool SetScaleOrientation( const SGVECTOR& aScaleAxis, double aAngle );
    bool SetRotation( const SGVECTOR& aRotationAxis, double aAngle );
    bool SetScale( const SGPOINT& aScale );
    bool SetScale( double aScale );
    bool SetCenter( const SGPOINT& aCenter );
    bool SetTranslation( const SGPOINT& aTranslation );

    /* various base class functions not shown here */
};

‘IFSG_SHAPE’类似于 VRML2.0 Shape 节点; 它必须包含单个子节点或引用 FACESET 节点,并且可以包含单个子节点或引用 APPEARANCE 节点。

ifsg_shape.h
/**
 * Class IFSG_SHAPE
 * is the wrapper for the SGSHAPE class
 */

class IFSG_SHAPE : public IFSG_NODE
{
public:
    IFSG_SHAPE( bool create );
    IFSG_SHAPE( SGNODE* aParent );
    IFSG_SHAPE( IFSG_NODE& aParent );

    /* various base class functions not shown here */
};

‘IFSG_APPEARANCE’类似于 VRML2.0 Appearance 节点,但目前它只代表包含 Material 节点的 Appearance 节点的等价物。

ifsg_appearance.h
class IFSG_APPEARANCE : public IFSG_NODE
{
public:
    IFSG_APPEARANCE( bool create );
    IFSG_APPEARANCE( SGNODE* aParent );
    IFSG_APPEARANCE( IFSG_NODE& aParent );

    bool SetEmissive( float aRVal, float aGVal, float aBVal );
    bool SetEmissive( const SGCOLOR* aRGBColor );
    bool SetEmissive( const SGCOLOR& aRGBColor );

    bool SetDiffuse( float aRVal, float aGVal, float aBVal );
    bool SetDiffuse( const SGCOLOR* aRGBColor );
    bool SetDiffuse( const SGCOLOR& aRGBColor );

    bool SetSpecular( float aRVal, float aGVal, float aBVal );
    bool SetSpecular( const SGCOLOR* aRGBColor );
    bool SetSpecular( const SGCOLOR& aRGBColor );

    bool SetAmbient( float aRVal, float aGVal, float aBVal );
    bool SetAmbient( const SGCOLOR* aRGBColor );
    bool SetAmbient( const SGCOLOR& aRGBColor );

    bool SetShininess( float aShininess );
    bool SetTransparency( float aTransparency );

    /* various base class functions not shown here */

    /* the following functions make no sense within an
       appearance node and always return a failure code

        bool AddRefNode( SGNODE* aNode );
        bool AddRefNode( IFSG_NODE& aNode );
        bool AddChildNode( SGNODE* aNode );
        bool AddChildNode( IFSG_NODE& aNode );
     */
};

‘IFSG_FACESET’类似于包含 IndexedFaceSet 节点的 VRML2.0 Geometry 节点。 它必须包含单个子节点或引用 COORDS 节点,单个子 COORDINDEX 节点以及单个子节点或引用 NORMALS 节点; 另外可能有一个子节点或引用 COLORS 节点。 提供简化的法线计算功能以帮助用户将正常值分配给表面。 与 VRML2.0 模拟的偏差如下:

  1. 法线始终是每个顶点。

  2. 颜色总是每个顶点。

  3. 坐标索引集必须仅描述三角形面。

ifsg_faceset.h
/**
 * Class IFSG_FACESET
 * is the wrapper for the SGFACESET class
 */

class IFSG_FACESET : public IFSG_NODE
{
public:
    IFSG_FACESET( bool create );
    IFSG_FACESET( SGNODE* aParent );
    IFSG_FACESET( IFSG_NODE& aParent );

    bool CalcNormals( SGNODE** aPtr );

    /* various base class functions not shown here */
};
ifsg_coords.h
/**
 * Class IFSG_COORDS
 * is the wrapper for SGCOORDS
 */

class IFSG_COORDS : public IFSG_NODE
{
public:
    IFSG_COORDS( bool create );
    IFSG_COORDS( SGNODE* aParent );
    IFSG_COORDS( IFSG_NODE& aParent );

    bool GetCoordsList( size_t& aListSize, SGPOINT*& aCoordsList );
    bool SetCoordsList( size_t aListSize, const SGPOINT* aCoordsList );
    bool AddCoord( double aXValue, double aYValue, double aZValue );
    bool AddCoord( const SGPOINT& aPoint );

    /* various base class functions not shown here */

    /* the following functions make no sense within a
       coords node and always return a failure code

        bool AddRefNode( SGNODE* aNode );
        bool AddRefNode( IFSG_NODE& aNode );
        bool AddChildNode( SGNODE* aNode );
        bool AddChildNode( IFSG_NODE& aNode );
     */
};

‘IFSG_COORDINDEX’类似于 VRML2.0 coordIdx[] 集,除了它必须专门描述三角形面,这意味着索引的总数可以被3整除。

ifsg_coordindex.h
/**
 * Class IFSG_COORDINDEX
 * is the wrapper for SGCOORDINDEX
 */

class IFSG_COORDINDEX : public IFSG_INDEX
{
public:
    IFSG_COORDINDEX( bool create );
    IFSG_COORDINDEX( SGNODE* aParent );
    IFSG_COORDINDEX( IFSG_NODE& aParent );

    bool GetIndices( size_t& nIndices, int*& aIndexList );
    bool SetIndices( size_t nIndices, int* aIndexList );
    bool AddIndex( int aIndex );

    /* various base class functions not shown here */

    /* the following functions make no sense within a
       coordindex node and always return a failure code

        bool AddRefNode( SGNODE* aNode );
        bool AddRefNode( IFSG_NODE& aNode );
        bool AddChildNode( SGNODE* aNode );
        bool AddChildNode( IFSG_NODE& aNode );
     */
};

‘IFSG_NORMALS’相当于 VRML2.0 Normals 节点。

ifsg_normals.h
/**
 * Class IFSG_NORMALS
 * is the wrapper for the SGNORMALS class
 */

class IFSG_NORMALS : public IFSG_NODE
{
public:
    IFSG_NORMALS( bool create );
    IFSG_NORMALS( SGNODE* aParent );
    IFSG_NORMALS( IFSG_NODE& aParent );

    bool GetNormalList( size_t& aListSize, SGVECTOR*& aNormalList );
    bool SetNormalList( size_t aListSize, const SGVECTOR* aNormalList );
    bool AddNormal( double aXValue, double aYValue, double aZValue );
    bool AddNormal( const SGVECTOR& aNormal );

    /* various base class functions not shown here */

    /* the following functions make no sense within a
       normals node and always return a failure code

        bool AddRefNode( SGNODE* aNode );
        bool AddRefNode( IFSG_NODE& aNode );
        bool AddChildNode( SGNODE* aNode );
        bool AddChildNode( IFSG_NODE& aNode );
     */
};

‘IFSG_COLORS’类似于 VRML2.0 colors[] 集。

ifsg_colors.h
/**
 * Class IFSG_COLORS
 * is the wrapper for SGCOLORS
 */

class IFSG_COLORS : public IFSG_NODE
{
public:
    IFSG_COLORS( bool create );
    IFSG_COLORS( SGNODE* aParent );
    IFSG_COLORS( IFSG_NODE& aParent );

    bool GetColorList( size_t& aListSize, SGCOLOR*& aColorList );
    bool SetColorList( size_t aListSize, const SGCOLOR* aColorList );
    bool AddColor( double aRedValue, double aGreenValue, double aBlueValue );
    bool AddColor( const SGCOLOR& aColor );

    /* various base class functions not shown here */

    /* the following functions make no sense within a
       normals node and always return a failure code

        bool AddRefNode( SGNODE* aNode );
        bool AddRefNode( IFSG_NODE& aNode );
        bool AddChildNode( SGNODE* aNode );
        bool AddChildNode( IFSG_NODE& aNode );
     */
};

其余的 API 函数在‘ifsg_api.h’中定义如下:

ifsg_api.h
namespace S3D
{
    /**
     * Function GetLibVersion retrieves version information of the
     * kicad_3dsg library
     */
    SGLIB_API void GetLibVersion( unsigned char* Major, unsigned char* Minor,
                                  unsigned char* Patch, unsigned char* Revision );

    // functions to extract information from SGNODE pointers
    SGLIB_API S3D::SGTYPES GetSGNodeType( SGNODE* aNode );
    SGLIB_API SGNODE* GetSGNodeParent( SGNODE* aNode );
    SGLIB_API bool AddSGNodeRef( SGNODE* aParent, SGNODE* aChild );
    SGLIB_API bool AddSGNodeChild( SGNODE* aParent, SGNODE* aChild );
    SGLIB_API void AssociateSGNodeWrapper( SGNODE* aObject, SGNODE** aRefPtr );

    /**
     * Function CalcTriNorm
     * returns the normal vector of a triangle described by vertices p1, p2, p3
     */
    SGLIB_API SGVECTOR CalcTriNorm( const SGPOINT& p1, const SGPOINT& p2, const SGPOINT& p3 );

    /**
     * Function WriteCache
     * writes the SGNODE tree to a binary cache file
     *
     * @param aFileName is the name of the file to write
     * @param overwrite must be set to true to overwrite an existing file
     * @param aNode is any node within the node tree which is to be written
     * @return true on success
     */
    SGLIB_API bool WriteCache( const char* aFileName, bool overwrite, SGNODE* aNode,
        const char* aPluginInfo );

    /**
     * Function ReadCache
     * reads a binary cache file and creates an SGNODE tree
     *
     * @param aFileName is the name of the binary cache file to be read
     * @return NULL on failure, on success a pointer to the top level SCENEGRAPH node;
     * if desired this node can be associated with an IFSG_TRANSFORM wrapper via
     * the IFSG_TRANSFORM::Attach() function.
     */
    SGLIB_API SGNODE* ReadCache( const char* aFileName, void* aPluginMgr,
        bool (*aTagCheck)( const char*, void* ) );

    /**
     * Function WriteVRML
     * writes out the given node and its subnodes to a VRML2 file
     *
     * @param filename is the name of the output file
     * @param overwrite should be set to true to overwrite an existing VRML file
     * @param aTopNode is a pointer to a SCENEGRAPH object representing the VRML scene
     * @param reuse should be set to true to make use of VRML DEF/USE features
     * @return true on success
     */
    SGLIB_API bool WriteVRML( const char* filename, bool overwrite, SGNODE* aTopNode,
                    bool reuse, bool renameNodes );

    // NOTE: The following functions are used in combination to create a VRML
    // assembly which may use various instances of each SG* representation of a module.
    // A typical use case would be:
    // 1. invoke 'ResetNodeIndex()' to reset the global node name indices
    // 2. for each model pointer provided by 'S3DCACHE->Load()', invoke 'RenameNodes()' once;
    //    this ensures that all nodes have a unique name to present to the final output file.
    //    Internally, RenameNodes() will only rename the given node and all Child subnodes;
    //    nodes which are only referenced will not be renamed. Using the pointer supplied
    //    by 'S3DCACHE->Load()' ensures that all nodes but the returned node (top node) are
    //    children of at least one node, so all nodes are given unique names.
    // 3. if SG* trees are created independently of S3DCACHE->Load() the user must invoke
    //    RenameNodes() as appropriate to ensure that all nodes have a unique name
    // 4. create an assembly structure by creating new IFSG_TRANSFORM nodes as appropriate
    //    for each instance of a component; the component base model as returned by
    //    S3DCACHE->Load() may be added to these IFSG_TRANSFORM nodes via 'AddRefNode()';
    //    set the offset, rotation, etc of the IFSG_TRANSFORM node to ensure correct
    // 5. Ensure that all new IFSG_TRANSFORM nodes are placed as child nodes within a
    //    top level IFSG_TRANSFORM node in preparation for final node naming and output
    // 6. Invoke RenameNodes() on the top level assembly node
    // 7. Invoke WriteVRML() as normal, with renameNodes = false, to write the entire assembly
    //    structure to a single VRML file
    // 8. Clean up by deleting any extra IFSG_TRANSFORM wrappers and their underlying SG*
    //    classes which have been created solely for the assembly output

    /**
     * Function ResetNodeIndex
     * resets the global SG* class indices
     *
     * @param aNode may be any valid SGNODE
     */
    SGLIB_API void ResetNodeIndex( SGNODE* aNode );

    /**
     * Function RenameNodes
     * renames a node and all children nodes based on the current
     * values of the global SG* class indices
     *
     * @param aNode is a top level node
     */
    SGLIB_API void RenameNodes( SGNODE* aNode );

    /**
     * Function DestroyNode
     * deletes the given SG* class node. This function makes it possible
     * to safely delete an SG* node without associating the node with
     * its corresponding IFSG* wrapper.
     */
    SGLIB_API void DestroyNode( SGNODE* aNode );

    // NOTE: The following functions facilitate the creation and destruction
    // of data structures for rendering

    /**
     * Function GetModel
     * creates an S3DMODEL representation of aNode (raw data, no transforms)
     *
     * @param aNode is the node to be transcribed into an S3DMODEL representation
     * @return an S3DMODEL representation of aNode on success, otherwise NULL
     */
    SGLIB_API S3DMODEL* GetModel( SCENEGRAPH* aNode );

    /**
     * Function Destroy3DModel
     * frees memory used by an S3DMODEL structure and sets the pointer to
     * the structure to NULL
     */
    SGLIB_API void Destroy3DModel( S3DMODEL** aModel );

    /**
     * Function Free3DModel
     * frees memory used internally by an S3DMODEL structure
     */
    SGLIB_API void Free3DModel( S3DMODEL& aModel );

    /**
     * Function Free3DMesh
     * frees memory used internally by an SMESH structure
     */
    SGLIB_API void Free3DMesh( SMESH& aMesh );

    /**
     * Function New3DModel
     * creates and initializes an S3DMODEL struct
     */
    SGLIB_API S3DMODEL* New3DModel( void );

    /**
     * Function Init3DMaterial
     * initializes an SMATERIAL struct
     */
    SGLIB_API void Init3DMaterial( SMATERIAL& aMat );

    /**
     * Function Init3DMesh
     * creates and initializes an SMESH struct
     */
    SGLIB_API void Init3DMesh( SMESH& aMesh );
};

有关 Scenegraph API 的实际使用示例,请参阅上面的《advanced-3d-plugin,Advanced 3D Plugin tutorial》,以及KiCad VRML1,VRML2 和 X3D 解析器。