VOP/VOP_Switch.C

/*
 * Copyright (c) 2013
 *      Side Effects Software Inc.  All rights reserved.
 *
 * Redistribution and use of Houdini Development Kit samples in source and
 * binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. The name of Side Effects Software may not be used to endorse or
 *    promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdlib.h>
#include <UT/UT_DSOVersion.h>
#include <UT/UT_NTStreamUtil.h>
#include <PRM/PRM_Include.h>
#include <OP/OP_OperatorTable.h>
#include <OP/OP_Input.h>
#include <VOP/VOP_Operator.h>
#include "VOP_Switch.h"

using namespace HDK_Sample;

void
newVopOperator(OP_OperatorTable *table)
{
    OP_Operator         *op;

    // Create a new VOP_Operator which describes the operator we are
    // building. The parameters to this function are similar to the
    // OP_Operator constructor except for the vopnet mask, and the
    // last parameter, which specifies the number of outputs from
    // this operator.
    op = new VOP_Operator("hdkswitch",                  // internal name
                          "HDK Switch",                 // UI name
                          VOP_Switch::myConstructor,    // How to create one
                          VOP_Switch::myTemplateList,   // parm definitions
                          0,                            // Min # of inputs
                          VOP_VARIABLE_INOUT_MAX,       // Max # of inputs
                          "*",                          // vopnet mask
                          0,                            // Local variables
                          OP_FLAG_UNORDERED,            // Special flags
                          1);                           // # of outputs

    table->addOperator(op);
}

static const char       *theInputRoot = "input";
static const char       *theOutputName = "result";
static PRM_Name          theSwitcherName("switcher", "Switcher Index");
static PRM_Name          theOutOfBoundsName("outofbounds",
                                            "Out Of Bounds Behavior");
static PRM_Name          theOutOfBoundsChoices[] = 
{
    PRM_Name("last", "Output Last Input Value"),
    PRM_Name("zero", "Output Zero"),
    PRM_Name()
};
enum 
{
    VOP_SWITCH_OOB_LAST,
    VOP_SWITCH_OOB_ZERO
};
static PRM_ChoiceList    theOutOfBoundsMenu(PRM_CHOICELIST_SINGLE,
                                            theOutOfBoundsChoices);

OP_Node *
VOP_Switch::myConstructor(OP_Network *net, const char *name, OP_Operator *entry)
{
    return new VOP_Switch(net, name, entry);
}

PRM_Template             VOP_Switch::myTemplateList[] = 
{
    PRM_Template(PRM_INT,       1, &theSwitcherName, PRMzeroDefaults),
    PRM_Template(PRM_ORD,       1, &theOutOfBoundsName, PRMzeroDefaults,
                                &theOutOfBoundsMenu),
    // List terminator
    PRM_Template()      
};

VOP_Switch::VOP_Switch(OP_Network *parent, const char *name, OP_Operator *entry)
    : VOP_Node(parent, name, entry)
{
}

VOP_Switch::~VOP_Switch()
{
}

unsigned
VOP_Switch::disableParms()
{
    int          changed = 0;

    // Disable the switcher index parameter if the first input is connected
    // because the input value will be used instead of the parameter value.
    changed += enableParm(theSwitcherName.getToken(), getInput(0) == 0);

    return changed;
}

void
VOP_Switch::getCode(UT_String &codestr, VOP_CodeVarMapperContext *context)
{
    // If there isn't at least one input connected after the switch index
    // input, then this node generates no code.
    if (getNumVisibleInputs() > 2)
    {
        UT_OStrStream    os;
        UT_String        inputName, outputName;
        int              outofbounds = OUTOFBOUNDS();
        int              i, j, next, first = 1;

        // The code generated by this node is a series of if/then/else
        // statements. Each inputs has a chance to be assigned to the
        // output value depending on the switch index value. The switch
        // index may be defined by an input or by a parameter value if no
        // input is connected.
        getOutputName(outputName, 0);
        for( i = getConnectedInputIndex(0), j = 0; i >= 0; i = next, j++ )
        {
            // For each connected input after the first one (the switch index
            // input), we output code like:
            // if( $switcher == 0 )
            //     $result = $input1;
            // else if( $switcher == 1 )
            //     $result = $input2;
            // else
            //     $result = $input3;
            //
            // Note that the inputs and output names are preceded by "$" so
            // the code generator will use unique variables names, or the
            // correct input variable names matching the connected inputs.
            // The $switcher value will be expanded by the code generator to
            // use either the matching input variable name or the switcher
            // parameter value (if no input is connected).
            next = getConnectedInputIndex(i);
            getInputName(inputName, i);
            if( next >= 0 || outofbounds == VOP_SWITCH_OOB_ZERO )
            {
                if( !first )
                    os << "else ";
                os << "if( $" << theSwitcherName.getToken();
                os << " == " << j << " )" << endl;
                os << "    ";
            }
            else if( !first )
            {
                os << "else" << endl << "    ";
            }
            os << "$" << outputName << " = $" << inputName << ";" << endl;
            first = 0;
        }
        // If we are asked to use the first input for out of bounds switch
        // indices, we need to append a final else clause to use the first
        // input value.
        if( outofbounds == VOP_SWITCH_OOB_ZERO )
        {
            VOP_Type     outputType = getOutputType(0);

            if( !first )
                os << "else" << endl << "    ";
            os << "$" << outputName << " = ";
            os << myLanguage->getEmptyConstantString(outputType) << ";" << endl;
        }
        os << ends;
        codestr.harden(os.str());
        os.rdbuf()->freeze(0);
    }
}

const char *
VOP_Switch::inputLabel(unsigned idx) const
{
    if (idx >= orderedInputs())
    {
        static UT_String         theLabel;
        char                     numstr[UT_NUMBUF];

        // Inputs after the first one are just numbered starting at 1.
        theLabel = "Input Number ";
        UT_String::itoa(numstr, idx + 1 - orderedInputs());
        theLabel += numstr;

        return theLabel;
    }
    else
        return theSwitcherName.getLabel();
}

const char *
VOP_Switch::outputLabel(unsigned idx) const
{
    UT_ASSERT(idx == 0);

    return "Chosen Value";
}

void
VOP_Switch::getInputNameSubclass(UT_String &in, int idx) const
{
    if (idx >= orderedInputs())
    {
        char     numstr[UT_NUMBUF];

        // Inputs after the first one are just numbered starting at 1.
        in = theInputRoot;
        UT_String::itoa(numstr, idx + 1 - orderedInputs());
        in += numstr;
    }
    else
        in = theSwitcherName.getToken();
}

int
VOP_Switch::getInputFromNameSubclass(const UT_String &in) const
{
    int          inputnum = -1;

    // The switcher input is always first.
    if( in == theSwitcherName.getToken() )
        return 0;

    // Use the numeric suffix on the input name to determine the input index.
    if( !strncmp(in, theInputRoot, strlen(theInputRoot)) )
        inputnum = ::atoi((const char *)in + strlen(theInputRoot));

    return inputnum - 1 + orderedInputs();
}

VOP_Type
VOP_Switch::getInputTypeSubclass(int idx)
{
    VOP_Node    *vop;
    OP_Input    *input;
    VOP_Type     vextype = VOP_TYPE_UNDEF;

    if( idx >= orderedInputs() )
    {
        // For any input past our ordered inputs, all input types are the
        // same - whatever is plugged into the first variable input.
        if( input = getInputReference(orderedInputs(), 0) )
        {
            vop = CAST_VOPNODE(input->getNode());
            if( vop )
                vextype = vop->getOutputType(input->getNodeOutputIndex());
        }
    }
    else
        vextype = myLanguage->conditionType(VOP_TYPE_INTEGER);
    
    return vextype;
}

void     
VOP_Switch::getAllowedInputTypesSubclass(unsigned idx, VOP_VopTypeArray &voptypes)
{
    VOP_Node    *vop;
    OP_Input    *input;

    if( idx >= orderedInputs() )
    {
        // For any input past our ordered inputs, all input types are the
        // same - whatever is plugged into the first variable input.
        if( input = getInputReference(orderedInputs(), 0) )
        {
            vop = CAST_VOPNODE(input->getNode());
            if( vop )
                voptypes.append(vop->getOutputType(input->getNodeOutputIndex()));
        }
    }
    else
        voptypes.append(myLanguage->conditionType(VOP_TYPE_INTEGER));
}

void
VOP_Switch::getOutputNameSubclass(UT_String &name, int idx) const
{
    UT_ASSERT(idx == 0);

    name = theOutputName;
}

VOP_Type
VOP_Switch::getOutputTypeSubclass(int idx)
{
    UT_ASSERT(idx == 0);

    // The output data type is the same as the data type of the first
    // input after the switch index.
    return getInputType(orderedInputs());
}

unsigned
VOP_Switch::getNumVisibleInputs() const
{
    int          max = nInputs();

    // Make sure there is always exactly one unconnected unordered input
    // visible.
    if( max < orderedInputs() )
        max = orderedInputs();

    return max + 1;
}

unsigned
VOP_Switch::orderedInputs() const
{
    // The first input (corresponding to the switch index) must always be
    // visible.
    return 1;
}

int
VOP_Switch::OUTOFBOUNDS()
{
    return evalInt(theOutOfBoundsName.getToken(), 0, 0.0f);
}


Generated on Mon Jan 28 00:26:26 2013 for HDK by  doxygen 1.5.9