Application Structure

TetGenLink is a Wolfram System application that makes the functions of TetGen available to the Wolfram Language. This is done with Wolfram LibraryLink, which allows TetGen to be used in a high-speed and low-memory fashion. This section will examine the details of how LibraryLink is used.

TetGen itself is written in C++. Its source code can be obtained from the TetGen website (http://tetgen.org). The Wolfram Language uses Version 1.4.3. The interface code is written in a mixture of C++ and the Wolfram Language, and this is shipped with TetGenLink.

You can always get to the location of TetGenLink by using $TetGenInstallationDirectory. This is shown in the following. First, you have to load the application.

This shows the location of TetGenLink for this version of the Wolfram System.

You might find this description useful for hooking up your own libraries to the Wolfram Language using LibraryLink.

Application Layout

TetGenLink is constructed as a Wolfram System application that contains the Wolfram Language code, documentation, shared libraries, example datasets, and C++ source code. In addition, the file PacletInfo.m is a descriptor for the application. A rough outline is shown below.

TetGenLink
PacletInfo.m
TetGenLink.m
Kernel
init.m
Documentation
English
guide, reference, and tutorial pages
ExampleData
sample datasets
LibraryResources
Windows
tetgenWolfram.dll
Other SystemIDs
Source
C
C++ source code

You can find more information on the application layout and how it can be built in the documentation for the Wolfram Workbench at http://www.wolfram.com/products/workbench, particularly in the sections that describe application development.

The Wolfram System will find different parts of the application as necessary. For example, when the Wolfram Language Documentation Center is used, this will connect to find the documentation. When the application is loaded with Needs["TetGenLink`"], this will load the init.m file. Finally, LibraryLink functions such as FindLibrary will locate the appropriate dynamic library for the $SystemID on which the Wolfram Language is running.

Wolfram Language Implementation

When TetGenLink is loaded with a command such as Needs["TetGenLink`"] or <<TetGenLink`, this will load the init.m file, which in turn loads the main Wolfram Language package TetGenLink.m. The latter follows the basic structure for a Wolfram Language package, with usage information, function exports, and implementation.

When this is loaded it defines a couple of symbols, as shown below.

$TetGenInstallationDirectory = DirectoryName[ $InputFileName]

$TetGenLibrary = FindLibrary[ "tetgenWolfram"]

To see the values of these symbols, you have to load the application.

This shows the location of the TetGenLink application. This is set up with $InputFileName.

This shows the location of the TetGenLink shared library for this version of the Wolfram Language. Notice how FindLibrary has resolved issues such as the $SystemID to use.

The next section loads various functions from the TetGen shared library. These all involve calls to LibraryFunctionLoad. Some, for example setPointsFun, involve sharing of data between the Wolfram Language and the library, which leads to a significant saving of time and memory. You can learn more about passing data to a library in the documentation on "MTensor Input Arguments".

LoadTetGen[] :=
Module[{},
instanceFun    = LibraryFunctionLoad[$TetGenLibrary,
                        "newTetGenInstance", {}, Integer];
deleteFun    = LibraryFunctionLoad[$TetGenLibrary,
                        "deleteTetGenInstance", {Integer}, Integer];
fileOperationFun = LibraryFunctionLoad[$TetGenLibrary,
                        "fileOperation", LinkObject, LinkObject];
        
getPointsFun = LibraryFunctionLoad[$TetGenLibrary,
                        "getPointList", {Integer}, {Real,_}];
setPointsFun = LibraryFunctionLoad[$TetGenLibrary,
                        "setPointList", {Integer, {Real, 2, "Shared"}}, Integer];

loading more functions...
]

The body of the package is concerned with calling functions in the library. For example, the implementation of TetGenCreate checks that the library has been loaded. Then it calls the instanceFun, which is defined in the library. One point here is that the library is not loaded until a function is actually used. It does not load the library when the package is loaded. The result of the function is a TetGenExpression expression holding the ID for the instance.

TetGenCreate[] :=
    Module[{},
        If[ needInitialization, LoadTetGen[]];
        TetGenExpression[ instanceFun[]]    
    ]

This invokes TetGenCreate. As can be seen, the result is a handle to the instance of the TetGen object held in the library. Any calls to the library will use this handle to get the actual instance.

Here is the Wolfram Language implementation of TetGenGetPoints. It is quite simple, extracting the ID from the TetGenExpression and passing it to the function in the TetGen library.

TetGenGetPoints[ TetGenExpression[ id_]] :=
    Module[{},
        getPointsFun[ id]
    ]

C++ Implementation

There are two parts to the C++ implementation. The source code for TetGen itself is found at the TetGen website (http://tetgen.org). This allows a version of TetGen to be built that can be used with other code. The other code provides an interface to the TetGen functions that can be used with LibraryLink. Its source is shipped with TetGenLink. You can find this in your TetGenLink installation using $TetGenInstallationDirectory.

To see this you must first load the application.

This is the folder that holds the C++ source for the interface.

The actual source files are shown in the following.

TetGen works by creating instances of objects that belong to the class tetgenio. These provide various methods, including adding data such as points and vertices from lists of data or from files, and carrying out various tetrahedralizations. The purpose of the interface code is support management of TetGen objects and access to their methods.

The source file tetgenInstance.cxx manages instances of TetGen objects. You can see some of its implementation in the following. This includes various header files for LibraryLink and for TetGen. It is going to use a hash_map from the C++ Standard Template Library to hold instances of the objects. To use this it needs to have code to load it for both the Visual C++ and GCC compilers. The hash_map allows an integer to be used as a key to find a tetgenio instance.

#include "tetgen.h"
#include "mathlink.h"
#include "WolframLibrary.h"

#include "tetgenWolframDLL.h"

#ifdef __GNUC__
#include <ext/hash_map>
namespace stdext
{
using namespace __gnu_cxx;
}
#else
#include <hash_map>
#endif

static stdext::hash_map<mint, tetgenio *> tetgenMap;

static mint tetgenListCnt = 0;

The next section contains various exports and declarations. Functions that are exported have to use a C naming scheme so they can be found by LibraryFunctionLoad; consequently, they use EXTERN_C. In addition, on Windows they need to be exported from the DLL into which they will be placed; consequently, they use DLLEXPORT.

EXTERN_C DLLEXPORT mint WolframLibrary_getVersion( ) ;

EXTERN_C DLLEXPORT mint WolframLibrary_initialize(
WolframLibraryData libData);

EXTERN_C DLLEXPORT int newTetGenInstance(
WolframLibraryData libData, mint Argc, MArgument *Args, MArgument res);

EXTERN_C DLLEXPORT int deleteTetGenInstance(
WolframLibraryData libData, mint Argc, MArgument *Args, MArgument res);

The getTetGenInstance function takes an integer and uses the hash_map to return the corresponding tetgenio instance. It is used throughout the TetGen library but is not exported from the library.

tetgenio* getTetGenInstance( mint num)
{
return tetgenMap[num];
}

Here a new instance of a TetGen object is created by calling the tetgenio constructor. It creates a new integer to use as an ID, saves the instance in the hash_map, and returns the ID. This is called directly by the Wolfram Language function TetGenCreate.

DLLEXPORT int newTetGenInstance(
WolframLibraryData libData, mint Argc, MArgument *Args, MArgument res)
{
mint newID = tetgenListCnt++;
tetgenMap[newID] = new tetgenio();
MArgument_setInteger(res, newID);
return 0;
}

Building the TetGen Library

The TetGenLink library is written in very standard C++, so you could build it in a number of ways, such as a makefile or a project file. The description here will show how to build directly from the Wolfram Language using the CCompilerDriver package. One advantage of this approach is that it will work for a variety of different compilers running on different types of machines.

There are a couple of issues with the build. First, one of the TetGen source files needs to be compiled with different settings. Second, the TetGen source used in TetGenLink has been modified to allow a function to get messages. The description here will show how to build with unmodified TetGen sources that can be found at the TetGen website (http://tetgen.org). The version used was TetGen 1.4.3.

To do the build you need to collect the TetGen sources along with the interface sources into a single directory. In this example, the symbol sourceDir gives the directory for the source, and outputDir gives the directory for the output.

You can see the actual source files as below.

Now, the CCompilerDriver` package has to be loaded.

The following sets up definitions for the input files as well as the file that is going to be compiled with no optimization.

The setting for not optimizing a file is different for different compilers. The following selects a value that will work for Visual Studio C++ and GCC compilers.

Now you can actually create the object file, with a call to CreateObjectFile.

Now you can build the library itself with CreateLibrary. The NOMESSAGEFUNCTION definition allows the library to be built with unmodified sources.

Modifying the Source

The modification to tetgen.cxx is quite simple. Its purpose is to allow messages generated by TetGen to appear in the Wolfram System. These normally appear with printf, so the change is to overload printf to capture the text and pass it back to the Wolfram System, as done in the following.

#define printf clientprintf

typedef void (*addMessage)( char*);

static addMessage messageFun = NULL;

void setMessageFunction(addMessage fun)
{
    messageFun = fun;
}


static void clientprintf( const char* mess, ...)
{
    char messBuff[ 4096];
    va_list ap;
    va_start(ap, mess);
    vsprintf( messBuff, mess, ap);
    va_end(ap);
    if ( messageFun != NULL) {
        messageFun( messBuff);
    }
}

If you add this code, you will need to remove the NOMESSAGEFUNCTION definition when building the library.