./features/writing plugins
Download Features Source code Forum Ask for support

Write your own plugin

Analysis Situs allows for extensions via Tcl plugins. On startup, it checks the contents of asi-plugins directory located in the installation directory of the software. Analysis Situs attempts to load all dynamic libraries it finds in that directory. For being a valid plugin, a dynamic library should satisfy specific rules. Analysis Situs tries to call a function named PLUGINFACTORY. If no such function exists, the corresponding candidate library is skipped as a non-plugin library.

Why plugins?

Analysis Situs is open-source software, but you are not obliged to release your derived works under the open-source conditions (because we use the BSD license). Therefore, you might want to:

  1. Purchase one of the commercial add-ons we are developing on top of Analysis Situs.
  2. Write your own code to customize or enrich Analysis Situs to better fit your needs.

For both scenarios, plugins are the answer. You can use the native UI workbench and feed it with your commands. Or you can load our native commercial add-ons to the workbench to unlock algorithms that are not distributed under the open-source conditions.

Code architecture

There is a best practice we encourage you to follow when extending Analysis Situs. Let's imagine you are going to develop a library of your "know-how" algorithms named MyAlgo. It is a good idea to have two libraries, though:

  1. MyAlgo for the algorithms.
  2. MyAlgoCmd for the Tcl bindings.

Having Tcl bindings in MyAlgoCmd would allow for running your Tcl-agnostic algorithms MyAlgo from within the UI workbench of Analysis Situs. In Analysis Situs, we put the entry points of all plugins to the binding libraries having "cmd" in their names.

Minimal example

We now provide listing for the minimal version of MyAlgoCmd code that can be loaded into Analysis Situs as a plugin. We mentioned above the complementary MyAlgo library, but it is not necessarily needed for test. What you need to get started is a couple C++ files (header and implementation):

  1. MyAlgoCmd.h
  2. MyAlgoCmd.cpp

Here is a header file MyAlgoCmd.h:

#ifndef MyAlgoCmd_h
#define MyAlgoCmd_h

#ifdef MyAlgoCmd_EXPORTS
  #define MyAlgoCmd_EXPORT __declspec(dllexport)
#else
  #define MyAlgoCmd_EXPORT __declspec(dllimport)
#endif

// asiTcl includes
#include <asiTcl_Interp.h>

// asiUI includes
#include <asiUI_CommonFacilities.h>

//! Custom commands.
class MyAlgoCmd
{
public:

  MyAlgoCmd_EXPORT static void
    Factory(const Handle(asiTcl_Interp)&      interp,
            const Handle(Standard_Transient)& data);

public:

  static Handle(asiUI_CommonFacilities) cf;

};

#endif

The header file declares a class that will serve as a provider of custom Tcl commands. You need to have the Factory() method there, which will be invoked automatically on dynamic loading. Notice also the static instance of the "common facilities" handle. It contains the pointers to all UI widgets and batch data structures your plugin is given access to.

The minimal implementation MyAlgoCmd.cpp may look as follows:

// Own include
#include <MyAlgoCmd.h>

// asiTcl includes
#include <asiTcl_PluginMacro.h>

Handle(asiUI_CommonFacilities) MyAlgoCmd::cf = nullptr;

//-----------------------------------------------------------------------------

int MYALGO_Test(const Handle(asiTcl_Interp)& interp,
                int                          argc,
                const char**                 argv)
{
  interp->GetProgress().SendLogMessage(LogInfo(Normal) << "This is a test.");
  return TCL_OK;
}

//-----------------------------------------------------------------------------

void MyAlgoCmd::Factory(const Handle(asiTcl_Interp)&      interp,
                        const Handle(Standard_Transient)& data)
{
  static const char* group = "MyAlgoCmd";

  /* Initialize common facilities. */
  MyAlgoCmd::cf = Handle(asiUI_CommonFacilities)::DownCast(data);
  //
  if ( cf.IsNull() )
  {
    return; // Error.
  }

  /* Add custom commands. */
  interp->AddCommand("myalgo-test",
    //
    "myalgo-test\n"
    "\t Test.",
    //
    __FILE__, group, MYALGO_Test);
}

// Declare entry point PLUGINFACTORY.
ASIPLUGIN(MyAlgoCmd)

Notice that the value of the static group variable should be exactly the same as the name of the dynamic library (i.e., "MyAlgoCmd" in the listing above).

Configuration

We advice using CMake for plugins, because it is a native configuration system for Analysis Situs. Consider setting the Analysis Situs' asi-plugins directory as INSTALL_DIR of your plugin's project. This way you will have the runnable software customized by your commands with minimal effort.

Troubleshooting

Plugin does not load

If your plugin does not load, do the following:

  1. Check that asi-plugins directory exists in the installation folder of Analysis Situs and contains your plugin libraries.
  2. Check that you have all dependencies in your PATH (Windows) or LD_LIBRARY_PATH (Linux) variable.

For debugging the missing runtime libs on Windows, you may want to use the depends.exe utility. To ensure that you have your development environment configured before running the Dependency Walker, consider launching it right from within IDE:

Among the tons of irrelevant errors, you will be able to find a missing runtime dependency:

Just keep in mind that your dynamic library may fail to load not because of itself, but because it has its own missing dependency.