Writing Custom Expression Plugins
Writing Custom Expression Plugins
The steps are:
- Create a ".cpp" source file with one or more functions.
- Define an SeExprPluginInit function.
- Compile the plugin.
- 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 "SeExprFunc.h"
#include "SeExprBuiltins.h"
double myfunc(double a,
double b,
double c)
{
return a * b + c;
}
2. Define an SeExprPluginInit function.
This is also in myfunc.cpp:
extern "C" void
SeExprPluginInit(SeExprFunc::Define define)
{
define("myfunc", myfunc);
}
3. Compile the plugin.
Here's a sample Makefile:
myfunc.so : myfunc.cpp $(shell pathfinder find_first "lib64/libSeExpr.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/libSeExpr.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 libSeExpr.so to libSeExpr.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 SeVec3d&)
|
scalar result, 2 vector args |
double myfunc(const
SeVec3d&, const SeVec3d&)
|
vector result, 1 vector arg |
SeVec3d myfunc(const
SeVec3d&)
|
vector result, 2 vector args |
SeVec3d myfunc(const
SeVec3d&, const SeVec3d&)
|
scalar result, variable number
of args
|
double myfunc(int n, double*
params)
|
scalar result, variable number
of vector args |
double myfunc(int n, const
SeVec3d* params)
|
vector result, variable number
of vector args |
SeVec3d myfunc(int n, const
SeVec3d* params)
|
Extension class (see note below)
|
class myfunc : public
SeExprFuncX {...} |
Note: the SeExprFuncX 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",
SeExprFunc(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",
SeExprFunc(foo, 4, -1)); // require at least 4 args
Detailed Example
#include "SeExprFunc.h"
#include "SeExprBuiltins.h"
// provide access to SeExpr built-in
functions
using namespace
SeExpr;
// wrapper to allow calling noise with
1 argument
double noise(const SeVec3d& P)
{
return noise(1, &P);
}
// wood(vector P)
double wood(const SeVec3d& P)
{
double noiseP = noise(P)*4;
double noiseP15 = noise(P*15)/16;
double noiseP50 = noise(P*50);
double L = length(P*10 + noiseP + noiseP15 +
SeVec3d(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 SeVec3d* args)
{
SeVec3d 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;
SeVec3d 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 SeExprPluginInit(SeExprFunc::Define define)
{
define("wood", wood);
define("brick3d", SeExprFunc(brick3d, 1,
2));
}