SeExpr
SeExpr Plugins
Writing Custom Expression Plugins

Writing Custom Expression Plugins

The steps are:
  1. Create a ".cpp" source file with one or more functions.
  2. Define an SeExprPluginInitV2 function.
  3. Compile the plugin.
  4. Add the plugin to the $SE_EXPR_PLUGINS path.

1. Create a ".cpp" source file with one or more functions.

Here's a trivial example, myfunc.cpp:

#include "SeExpr2/ExprFunc.h"
#include "SeExpr2/ExprBuiltins.h"

double myfunc(double a, double b, double c)
{
    return a * b + c;
}

2. Define an ExprPluginInitV2 function.

This is also in myfunc.cpp:

extern "C" void ExprPluginInitV2(ExprFunc::Define define)
{
    define("myfunc", myfunc);
}

3. Compile the plugin.

Here's a sample Makefile:

myfunc.so : myfunc.cpp $(shell pathfinder find_first "lib64/libSeExpr2.so")
    $(CXX) -O2 -I$(shell pathfinder find_package SeExpr)/include -shared -o $@ $^

Or, you can just run the compiler directly:

g++ -O2 -I$(shell pathfinder find_package SeExpr)/include -shared -o myfunc.so myfunc.cpp $(shell pathfinder find_first "lib64/libSeExpr2.so")

Note: there are both static (.a) and dynamic (.so) versions of the expression library available.  If you link with the static version, your plugin will be slightly larger, but you'll be insulated from changes to the expression library.  To use the static version, just change libSeExpr2.so to libSeExpr2.a on the command line above.

4. Add the plugin to the $SE_EXPR_PLUGINS path.

The $SE_EXPR_PLUGINS environment variable contains a colon-separated list of plugin filenames and/or names of directories containing plugins.  If a directory name is used, the plugin name must match the pattern "SeExpr*.so".  If the plugin is explicitly listed, it may be named anything.

setenv SE_EXPR_PLUGINS $PWD/myfunc.so

Note: the ctaddenv command can be used in a shell script to add the plugin without worrying whether SE_EXPR_PLUGINS is already defined or not:

setenv SE_EXPR_PLUGINS `ctaddenv SE_EXPR_PLUGINS $PWD/myfunc.so`

Supported function types:

scalar result, no args
double myfunc()
scalar result, 1 arg
double myfunc(double)
scalar result, 2 args double myfunc(double, double)
scalar result, 3 args double myfunc(double, double, double)
scalar result, 4 args double myfunc(double, double, double, double)
scalar result, 5 args double myfunc(double, double, double, double, double)
scalar result, 6 args double myfunc(double, double, double, double, double, double)
scalar result, 1 vector arg
double myfunc(const Vec3d&)
scalar result, 2 vector args double myfunc(const Vec3d&, const Vec3d&)
vector result, 1 vector arg Vec3d myfunc(const Vec3d&)
vector result, 2 vector args Vec3d myfunc(const Vec3d&, const Vec3d&)
scalar result, variable number of args
double myfunc(int n, double* params)
scalar result, variable number of vector args double myfunc(int n, const Vec3d* params)
vector result, variable number of vector args Vec3d myfunc(int n, const Vec3d* params)
Extension class (see note below)
class myfunc : public ExprFuncX {...}

Note: the ExprFuncX extension class can accept any type or number of args, including strings, and can produce a vector or scalar result.  Also, the extension function has access to the internal expression objects for caching data, etc.

For functions that take a variable number of arguments, the min and max argument count must be given when the function is registered:

define("brick3d", ExprFunc(brick3d, 1, 5)); // require 1..5 args

Note: the maxargs param can be set to -1 to allow an unlimited number of arguments:

define("foo", ExprFunc(foo, 4, -1)); // require at least 4 args

Detailed Example

#include "SeExpr2/ExprFunc.h"
#include "SeExpr2/ExprBuiltins.h"

// provide access to SeExpr built-in functions
using namespace SeExpr2;

// wrapper to allow calling noise with 1 argument
double noise(const Vec3d& P)
{
    return noise(1, &P);
}

// wood(vector P)
double wood(const Vec3d& P)
{
    double noiseP = noise(P)*4;
    double noiseP15 = noise(P*15)/16;
    double noiseP50 = noise(P*50);

    double L = length(P*10 + noiseP + noiseP15 + Vec3d(40,60,20));
    double Lf = fmod(L, 1);

    return mix(pow(noise(P*60 + noiseP) + noiseP15, 2) *
               cellnoise(L - 0.1) * 0.5 + 0.25,
               invert(pow(invert(noiseP50),2)),
               smoothstep(0, .1, Lf) *
               smoothstep(.2, 1, invert(Lf)));
}

// brick3d(vector P, float type=1), type can be 1 or 2.
double brick3d(int nargs, const Vec3d* args)
{
    Vec3d p = 0.0;
    int type = 1;
    switch (nargs) {
    case 2: type = int(args[1][0]);
    case 1: p = args[0];
    }

    double p0 = -0.05 + p[0]/31.8;
    double p1 = p[1]/24;
    double p2 = -0.05 + p[2]/31.8;
    double t1 = trunc(101 + (p1 + 100) * 7) / 2;
    double t2 = trunc(100 + (p1 + 100) * 7) / 2;
    if (type==1)
    {
        double val =
            smoothstep(0, 0.15, fmod((p0 + 100) * 3 + t1, 1)) *
            smoothstep(0, 0.15, fmod((p2 + 100) * 3 + t2, 1)) *
            smoothstep(0, 0.15, fmod((invert(p0) + 100) * 3 + t1, 1)) *
            smoothstep(0, 0.15, fmod((invert(p2) + 100) * 3 + t2, 1)) *
            smoothstep(0, 0.3,  fmod((p1 + 100) * 7, 1)) *
            smoothstep(0, 0.3,  fmod((invert(p1) + 100) * 7, 1));
        return pow( invert( val ), 8 );
    }
    else if (type==2)
    {
        float result;
        Vec3d cell;
        cell[0] = (p0 + 100) * 3 + t1;
        cell[1] = (p1 + 100) * 7;
        cell[2] = (p2 + 100) * 3 + t2;
        return cellnoise(cell);
    }
    return 0;
}

extern "C" void SeExprPluginInitV2(
SeExpr2::ExprFunc::Define define)
{
    define("wood", wood);
    define("brick3d", SeExpr2::ExprFunc(brick3d, 1, 2));
}