/**

@page houdini Houdini Cookbook

This cookbook provides code snippets that illustrate the use of some new
tools that simplify the construction of operators in Houdini.
It also shows how to write operators that use OpenVDB.


@section sHoudiniContents Contents
- @ref sUIConstruction
  - @ref sParmFactory
  - @ref sOpFactory
  - @ref sScopedInputLock
- @ref sOpenVDBOperators
  - @ref sListOfIncomingGrids
  - @ref sIteratingOverGrids
  - @ref sProcessingTypedGrids


@section sUIConstruction General operator construction
This section gives usage examples for some general helper classes
that aid in the construction of Houdini operators.
These helper classes are independent of OpenVDB and can be used in the
implementation of any type of operator.


@subsection sParmFactory ParmFactory and ParmList
The @hulink::ParmFactory ParmFactory@endlink provides a simplified
interface to define the parameters of an operator.
Invoking the @hulink::ParmFactory::get() get@endlink method on a
@b ParmFactory produces a new @b PRM_Template describing a single parameter.
For example,

@code
#include <houdini_utils/ParmFactory.h>
...

PRM_Template groupParm =
    houdini_utils::ParmFactory(PRM_STRING, "group", "Group")
    .setTooltip("Specify a subset of the input VDB grids to be processed.")
    .setDocumentation(
        "The subset of VDB grids to be processed"
        " (see [specifying volumes|/model/volumes#group] for details"
        " on selecting grids)")
    .setChoiceList(&houdini_utils::PrimGroupMenu)
    .get();

PRM_Template toleranceParm =
    houdini_utils::ParmFactory(PRM_FLT_J, "tolerance", "Pruning Tolerance")
    .setDefault(0.01)
    .setRange(PRM_RANGE_RESTRICTED, 0, PRM_RANGE_UI, 1)
    .get();
@endcode

Note that, using a @b ParmFactory, one need only specify those
attributes of a parameter (@b setDefaults, @b setTooltip, etc.)
that have non-default values.

By default, a parameter&rsquo;s tooltip is used to describe the parameter
on the Help page for its operator.
More detailed documentation in Houdini&rsquo;s
<A HREF="http://www.sidefx.com/docs/houdini/help/format">wiki markup</A>
format can be added with @hulink::ParmFactory::setDocumentation()
setDocumentation@endlink, as in the example above.
Call @b setDocumentation with a null pointer or with an empty string
to exclude a parameter from the Help page.

@b ParmFactory objects may be added directly to a
@hulink::ParmList ParmList@endlink, which among other things ensures that
the list of templates is properly null-terminated.
Typically, the @b ParmList is populated at the time the operator is registered,
as in the following example:
@code
#include <houdini_utils/ParmFactory.h>

void
newSopOperator(OP_OperatorTable* table)
{
    houdini_utils::ParmList parms;

    // Define a string-valued group name pattern parameter and add it to the list.
    parms.add(houdini_utils::ParmFactory(PRM_STRING, "group", "Group")
        .setTooltip("Specify a subset of the input VDB grids to be processed.")
        .setChoiceList(&houdini_utils::PrimGroupMenu));

    // Define a menu of verbosity levels.
    parms.add(houdini_utils::ParmFactory(PRM_ORD, "verbose", "Verbosity")
        .setDefault(PRMoneDefaults)
        .setChoiceListItems(PRM_CHOICELIST_SINGLE, {
            "quiet",    "Quiet", // token, label
            "verbose",  "Verbose",
            "verbose2", "More Verbose",
        }));

    // Define a menu from a dynamically-constructed list of items.
    {
        std::vector<std::string> items;
        for (int i = 0; i < openvdb::NUM_GRID_CLASSES; ++i) {
            openvdb::GridClass cls = openvdb::GridClass(i);
            items.push_back(openvdb::GridBase::gridClassToString(cls));   // token
            items.push_back(openvdb::GridBase::gridClassToMenuName(cls)); // label
        }

        parms.add(houdini_utils::ParmFactory(PRM_ORD, "gridclass", "Grid Class")
            .setDefault(PRMzeroDefaults)
            .setChoiceListItems(PRM_CHOICELIST_SINGLE, items));
    }

    ...
}
@endcode

@subsection Switchers
The @b ParmList provides a convenient way of defining switchers (tab menus):

@code
parms.beginSwitcher("switcher");

parms.addFolder("Tree Topology");

parms.add(houdini_utils::ParmFactory(PRM_HEADING, "nodes", "Nodes"));
parms.add(houdini_utils::ParmFactory(PRM_TOGGLE, "viewnodes", "View"));
...

parms.addFolder("Isosurface");

parms.add(houdini_utils::ParmFactory(PRM_HEADING, "surfacing", "Surfacing"));
parms.add(houdini_utils::ParmFactory(PRM_TOGGLE, "extractmesh", "Extract Mesh"));
...

parms.endSwitcher();
@endcode

The above generates the following UI:

@image html tabmenu.png

Switchers can also be nested:

@code
parms.beginSwitcher("switcher");
    parms.addFolder("A");
        parms.beginSwitcher("nested_switcher");
            parms.addFolder("1");
            parms.addFolder("2");
            parms.addFolder("3");
        parms.endSwitcher();
    parms.addFolder("B");
    ...
parms.endSwitcher();
@endcode

@subsection Multi-Parms
Multi-parms are dynamically-sized parameters that consist of a variable
number of child instances.
Each child instance is defined by a second @b ParmList that itself consists
of multiple parameters.
These parameters&rsquo; tokens must include a @c # character, which is
typically placed at the end of the token.

@code
houdini_utils::ParmList parms;
...

// Build the multi-parm's parameter list.
houdini_utils::ParmList multiParms;
multiParms.add(houdini_utils::ParmFactory(PRM_STRING, "gridname#", "Grid name")
    .setTooltip("Specify a name for this grid."));
...

// Create the multi-parm itself.
parms.add(houdini_utils::ParmFactory(PRM_MULTITYPE_LIST, "gridlist", "Grids")
    .setMultiparms(multiParms)
    .setDefault(PRMoneDefaults));
...
@endcode

The above generates the following UI:
@image html multiparm.png

A multi-parm&rsquo;s parameters are accessed by iterating through each
child instance:

@code
UT_String gridNameStr;

for (int i = 1, N = evalInt("gridlist", 0, 0); i <= N; ++i) {
    evalStringInst("gridname#", &i, gridNameStr, 0, time);
    ...
}
@endcode

Note that evaluating the multi-parm gives the number of child instances,
and that the instances are numbered starting from one.


@subsection sOpFactory OpFactory
The @hulink::OpFactory OpFactory@endlink is used in conjunction with a
@hulink::ParmList ParmList@endlink to register a new operator by adding it
to the @b OP_OperatorTable.
Among other things, the @b OpFactory ensures that the operator&rsquo;s
type name follows a consistent naming scheme, that its Help URL is set
correctly and that its inputs are labeled.
Continuing with the earlier @b newSopOperator example,

@code
#include <houdini_utils/ParmFactory.h>

void
newSopOperator(OP_OperatorTable* table)
{
    ...

    // Register this operator.
    houdini_utils::OpFactory(houdini_utils::OpPolicy(), "My SOP",
            MySOP::factory, parms, *table)
        .addInput("VDB grids to process")        // input 0
        .addOptionalInput("Reference geometry"); // input 1
}
@endcode

The first argument to the @b OpFactory constructor is an instance of the
@hulink::OpPolicy OpPolicy@endlink class (or a subclass thereof).
@b OpPolicy objects allow for customization of certain behaviors of
the @b OpFactory.
The base class specifies a policy for converting an English
operator name like <tt>"My SOP"</tt>, which appears in menus and other
UI elements, into an operator type name.
The default policy is simply to call @b UT_String::forceValidVariableName
on the English name.

If the policy does not specify the URL of a Help page, or if the
@b OpFactory is constructed without an @b OpPolicy, then documentation
for the operator in Houdini&rsquo;s
<A HREF="http://www.sidefx.com/docs/houdini/help/format">wiki markup</A>
format can be provided with @hulink::OpFactory::setDocumentation()
setDocumentation@endlink.
By default, documentation for the operator&rsquo;s parameters is generated
automatically and appended to the text provided with @b setDocumentation.

When a particular @b OpPolicy is to be used to register multiple operators,
it might be convenient to subclass @b OpFactory itself and provide a
constructor that automatically initializes the base class with the desired
policy.

If an operator ever needs to be renamed, call
@hulink::OpFactory::addAlias() OpFactory::addAlias@endlink
with the old name.  This will help to ensure that scene files in which
the operator was saved with the old name can still be read:

@code
houdini_utils::OpFactory("My Renamed SOP", MySOP::factory, parms, *table)
    .addInput("VDB grids to process")
    .addAlias("My SOP"); // old name
@endcode

If the operator name changed as a result of an @b OpPolicy change, supply
the old operator type name directly, with
@hulink::OpFactory::addAliasVerbatim() addAliasVerbatim@endlink:

@code
houdini_utils::OpFactory(houdini_utils::MyNewOpPolicy(), "My SOP",
        MySOP::factory, parms, *table)
    .addAliasVerbatim("My_Old_SOP_Type"); // old operator type name
@endcode

Among other @b OpFactory features,
@hulink::OpFactory::setObsoleteParms() OpFactory::setObsoleteParms@endlink
accepts an additional @b ParmList of parameters that are no longer in use
but that might still exist in older scene files:

@code
#include <houdini_utils/ParmFactory.h>

void
newSopOperator(OP_OperatorTable* table)
{
    // This boolean "draw" parameter has been replaced with a multi-state
    // "drawmode" parameter.
    houdini_utils::ParmList obsoleteParms;
    obsoleteParms.add(houdini_utils::ParmFactory(PRM_TOGGLE, "draw", "Draw"));

    houdini_utils::ParmList parms;

    parms.add(houdini_utils::ParmFactory(PRM_ORD, "drawmode", "Draw")
        .setChoiceListItems(PRM_CHOICELIST_SINGLE, {
            "none",      "Don't Draw",
            "wireframe", "Draw Wireframe",
            "shaded",    "Draw Shaded",
        }));

    // Register this operator.
    houdini_utils::OpFactory("My SOP", MySOP::factory, parms, *table)
        .addInput("Input geometry")
        .setObsoleteParms(obsoleteParms);
}
@endcode

Override @b OP_Node::resolveObsoleteParms to convert the values of
obsolete parameters into values of current parameters where appropriate.


@subsection sScopedInputLock ScopedInputLock
A @hulink::ScopedInputLock ScopedInputLock@endlink locks the inputs to
an operator and then automatically unlocks them when it (the lock
object) goes out of scope, even if an exception is thrown.

@code
#include <houdini_utils/ParmFactory.h>

OP_ERROR
MySOP::cookMySop(OP_Context& context)
{
    try {
        houdini_utils::ScopedInputLock lock(*this, context);
        //
        //  do cook work
        //
    } catch (std::exception& e) {
        addError(SOP_MESSAGE, e.what());
    }
    return error();
}
@endcode


@section sOpenVDBOperators OpenVDB SOP construction
OpenVDB SOPs are derived from the @hvdblink::SOP_NodeVDB SOP_NodeVDB@endlink
base class which, among other things, adds guide geometry and node-specific
info text.

@code
#include <houdini_utils/ParmFactory.h>
#include <openvdb_houdini/Utils.h>
#include <openvdb_houdini/SOP_NodeVDB.h>
#include <UT/UT_Interrupt.h>

class SOP_DW_OpenVDBTemplate: public openvdb_houdini::SOP_NodeVDB
{
public:
    SOP_DW_OpenVDBTemplate(OP_Network*, const char* name, OP_Operator*);
    ~SOP_DW_OpenVDBTemplate() override {};

    static OP_Node* factory(OP_Network*, const char* name, OP_Operator*);

    // Return true for a given input if the connector to the input
    // should be drawn dashed rather than solid.
    int isRefInput(unsigned idx) const override { return (idx == 1); }

protected:
    OP_ERROR cookVDBSop(OP_Context&) override;
    bool updateParmsFlags() override;
};
@endcode


@subsection sListOfIncomingGrids Selecting grids
A typical SOP will have at least one group name parameter for each of
its inputs.  These parameters should be defined as follows (note the
use of spare data to specify the input):

@code
houdini_utils::ParmList parms;

// Define a string-valued group name pattern parameter.
parms.add(houdini_utils::ParmFactory(PRM_STRING, "group", "Group")
    .setTooltip("Specify a subset of the input VDB grids to be processed.")
    .setChoiceList(&houdini_utils::PrimGroupMenu));

// Define a group name parameter associated with this operator's second input.
parms.add(houdini_utils::ParmFactory(PRM_STRING, "group", "Group")
    .setTooltip("Specify a subset of the input VDB grids to be processed.")
    .setChoiceList(&houdini_utils::PrimGroupMenu)
    .setSpareData(&SOP_Node::theSecondInput));
@endcode

Associated with each parameter is a menu of primitive group names.
Users can select one or more groups from the menu, or create new groups
on the fly using Houdini&rsquo;s <tt>\@<i>attr</i>=<i>value</i></tt> syntax.
For example, entering <tt>\@name="density*"</tt> as the group name
creates a new group comprising all input primitives whose name begins
with <tt>density</tt>.  Entering <tt>\@vdb_value_type=float</tt>
creates a new group of input grid primitives whose data type is @c float.
Users may enter multiple space-separated group names or grouping expressions.


@subsection sIteratingOverGrids Iterating over grids
In @b cookMySOP, the string value of a group parameter
is used to construct a @b GA_PrimitiveGroup.
(SOP_NodeVDB provides a convenience method,
@hvdblink::SOP_NodeVDB::matchGroup() SOP_NodeVDB::matchGroup@endlink,
to simplify this step and to handle errors in a standard way.)
The @b GA_PrimitiveGroup so constructed may be iterated over using
either a @hvdblink::VdbPrimCIterator VdbPrimCIterator@endlink, for
read-only access, or a @hvdblink::VdbPrimIterator VdbPrimIterator@endlink,
for read/write access:

@code
OP_ERROR
MySOP::cookVDBSOP(OP_Context& context)
{
    try {
        houdini_utils::ScopedInputLock lock(*this, context);

        const fpreal time = context.getTime();

        // This does a deep copy of native Houdini primitives
        // but only a shallow copy of VDB grids.
        duplicateSource(0, context);

        // Get the group of grids to process.
        UT_String groupStr;
        evalString(groupStr, "group", 0, time);
        const GA_PrimitiveGroup* group = matchGroup(*gdp, groupStr.toStdString());

        // Get other UI parameters.
        int verbose = evalInt("verbose", 0, time);

        UT_AutoInterrupt progress("Processing VDB grids");

        // For each VDB primitive in the selected group...
        for (openvdb_houdini::VdbPrimIterator it(gdp, group); it; ++it) {

            if (progress.wasInterrupted()) {
                throw std::runtime_error("processing was interrupted");
            }

            GU_PrimVDB* vdbPrim = *it;

            // Optionally get the primitive's name (or, if the name is empty,
            // the primitive's index).
            const UT_String gridName = it.getPrimitiveNameOrIndex();

            // If this primitive's grid is shared with other primitives, make
            // a deep copy of it.  If the grid is not going to be modified
            // in place (or when using GEOvdbProcessTypedGrid()--see below),
            // skip this step.
            vdbPrim->makeGridUnique();

            openvdb_houdini::Grid& grid = vdbPrim->getGrid();

            // Process the grid.
            // (Your code goes here.)

            // In cases where it is not possible to process the primitive's
            // grid in place, replace the grid with a new grid:
            // openvdb_houdini::GridPtr outputGrid = ...;
            // vdbPrim->setGrid(*outputGrid);
        }
    } catch (std::exception& e) {
        addError(SOP_MESSAGE, e.what());
    }
    return error();
}
@endcode


@subsection sProcessingTypedGrids Processing grids of different types
Recall that a @b Grid is a container for a transform, metadata and a
@b Tree, and that the @b Tree holds voxel data of a specific type
(@c bool, @c float, @c vec3s, etc.).
Whenever possible, try to write generic grid processing code.
That is, write code that can handle grids of more than one type
(@c int, @c float and @c double, say, instead of just @c float)
or, ideally, grids of arbitrary type.

Writing generic code can be tricky, but convenience functions
exist to make the job easier.
Use @b GEOvdbProcessTypedGrid to call a method or methods on
a primitive&rsquo;s grid, regardless of its type:

@code
MyGridProcessor proc;  // functor (see explanation below)
// Call a method on the primitive's grid, regardless of the grid's type.
// Note that by default, GEOvdbProcessTypedGrid() calls makeGridUnique()
// on the primitive if it is non-const.
GEOvdbProcessTypedGrid(*vdbPrim, proc);
@endcode

@b GEOvdbProcessTypedGrid accepts a primitive and a functor
(an object for which the call operator,
<tt>operator()()</tt>, is defined).
The functor&rsquo;s call operator must be templated on a single type
(the grid type) and must accept a single argument (a reference to a grid
of the template type).
The operator&rsquo;s return value is ignored, so it&rsquo;s best to
declare it @c void.
The following is a simple example of a functor that satisfies these conditions:

@code
struct PruneOp
{
    template<typename GridT>
    void operator()(GridT& grid) const { grid.prune(); }
};
@endcode

@b PruneOp can call the @b prune method on a grid of any type, but it&rsquo;s
necessary to know the specific type, because @b prune takes an optional
tolerance argument whose type is the type of the grid&rsquo;s voxel values.
(Note that grids also have a @b pruneGrid method that doesn&rsquo;t require
knowledge of the voxel value type.)

Because a functor is an object, it can have member variables.
This makes it possible for the functor to process more than one grid
at a time, even though it gets called with only one grid.
This example shows how one might compute the CSG union of two level set
grids, <i>A</i> and <i>B</i>, of the same type:

@code
#include <openvdb/tools/Composite.h> // for csgUnion()

struct CSGUnionOp
{
    // Pointer to the B grid
    // (non-const, because CSG operations modify both the A and B grids)
    openvdb_houdini::Grid* bGridBase;

    template<typename GridT>
    void operator()(GridT& aGrid) const
    {
        // Cast the generic B grid pointer to point to a grid of
        // the same type as the A grid.
        if (GridT* bGrid = UTvdbGridCast<GridT>(bGridBase)) {
            // Compute the union, storing the result in the A grid
            // and emptying the B grid.
            openvdb::tools::csgUnion(aGrid, *bGrid);
        }
    }
};


OP_ERROR
MySOP::cookVDBSop(OP_Context& context)
{
    ...
    // Retrieve the A and B grids from primitives on the input detail(s)
    // (non-const, because CSG operations modify both the A and B grids).
    openvdb_houdini::Grid
        &aGrid = aPrim->getGrid(),
        &bGrid = bPrim->getGrid();

    // CSG operations require the A and B grids to have the same type.
    if (aGrid.type() != bGrid.type()) {
        addError(SOP_MESSAGE, "grids have different types");
    } else {
        CSGUnionOp proc;

        // Hand the functor a generic pointer to the B grid
        // (whose concrete type we don't know yet).
        proc.bGridBase = &bGrid;

        // Apply the CSG operation, overwriting grid A (and emptying grid B).
        if (!GEOvdbProcessTypedGrid(*aPrim, proc)) {
            addError(SOP_MESSAGE, "failed to compute CSG union");
        }
        ...
    }
}
@endcode

Certain operations might make sense only for grids with scalar or with
vector voxel values.
Variants of @b GEOvdbProcessTypedGrid exist to handle those cases.
For example, @b GEOvdbProcessTypedGridScalar and @b GEOvdbProcessTypedGridVec3
invoke a functor only on grids of scalar or 3-vector types, respectively,
and they return @c false for and ignore grids of all other types.


<!--
To do:
- iterating over multiple inputs
- VDB source nodes
-->

*/
