OP_ERROR
SOP_PointsFromVoxels::cookMySop(OP_Context &context)
{
    bool                        cull, store;
    fpreal                      now, value;
    int                         rx, ry, rz;
    unsigned                    primnum;

    GA_Offset                   ptOff;
    GA_ROAttributeRef           input_attr_gah;
    GA_RWAttributeRef           attr_gah;
    GA_ROHandleS                input_attr_h;
    GA_RWHandleF                attr_h;

    const GU_Detail             *input_geo;
    const GEO_Primitive         *prim;
    const GEO_PrimVolume        *vol;

    UT_String                   attr_name;
    UT_Vector3                  pos;
    UT_VoxelArrayIteratorF      vit;

    now = context.getTime();

    if (lockInputs(context) >= UT_ERROR_ABORT)
    {
        return error();
    }

    // Get the primitive number.
    primnum = PRIM(now);

    // Check for culling.
    cull = CULL(now);

    store = STORE(now);

    // Clear out the detail since we only want our new points.
    gdp->clearAndDestroy();

    // Get the input geometry as read only.
    GU_DetailHandleAutoReadLock gdl(inputGeoHandle(0));
    input_geo = gdl.getGdp();

    // Primitive number is valid.
    if (primnum < input_geo->getNumPrimitives())
    {
        // Get the primitive we need.
        prim = input_geo->primitives()(primnum);

        // The primitive is a volume primitive.
        if (prim->getTypeId().get() == GEO_PRIMVOLUME)
        {
            // Get the actual PrimVolume.
            vol = (const GEO_PrimVolume *)prim;

            // Get a voxel read handle from the primitive.
            UT_VoxelArrayReadHandleF    vox(vol->getVoxelHandle());

            // Attach the voxel iterator to the handle.
            vit.setHandle(vox);

            if (store)
            {
                // Try to find a 'name' attribute.
                input_attr_gah = input_geo->findPrimitiveAttribute("name");

                if (input_attr_gah.isValid())
                {
                    // Get this primitive's name.
                    input_attr_h.bind(input_attr_gah.getAttribute());
                    attr_name = input_attr_h.get(primnum);
                }

                // No name, so just use 'value'.
                else
                {
                    attr_name = "value";
                }

                // Add a float point attribute to store the values.
                attr_gah = gdp->addFloatTuple(GA_ATTRIB_POINT, attr_name, 1);

                // Attach an attribute handle.
                attr_h.bind(attr_gah.getAttribute());
            }

            // Culling empty voxels.
            if (cull)
            {
                // Iterate over all the voxels.
                for (vit.rewind(); !vit.atEnd(); vit.advance())
                {
                    // The voxel value.
                    value = vit.getValue();

                    // Skip voxels with a value of 0.
                    if (value == 0)
                    {
                        continue;
                    }

                    // Convert the voxel index to a position.
                    vol->indexToPos(vit.x(), vit.y(), vit.z(), pos);

                    // Create a point and set it to the position of the
                    // voxel.
                    ptOff = gdp->appendPointOffset();
                    gdp->setPos3(ptOff, pos);

                    // Store the value if necessary.
                    if (store)
                    {
                        attr_h.set(ptOff, value);
                    }
                }
            }

            else
            {
                // Get the resolution of the volume.
                vol->getRes(rx, ry, rz);

                // Add points for each voxel.
                ptOff = gdp->appendPointBlock(rx * ry * rz);

                // Iterate over all the voxels.
                for (vit.rewind(); !vit.atEnd(); vit.advance())
                {
                    // Convert the voxel index to a position.
                    vol->indexToPos(vit.x(), vit.y(), vit.z(), pos);

                    // Set the position for the current offset.
                    gdp->setPos3(ptOff, pos);

                    // Get and store the value if necessary.
                    if (store)
                    {
                        value = vit.getValue();
                        attr_h.set(ptOff, value);
                    }

                    // Increment the offset since the block of points we
                    // created is guaranteed to be contiguous.
                    ptOff++;
                }
            }
        }
        // Primitive isn't a volume primitive.
        else
        {
            addError(SOP_MESSAGE, "Not a volume primitive.");
        }
    }
    // Picked a primitive number that is out of range.
    else
    {
        addWarning(SOP_MESSAGE, "Invalid source index. Index out of range.");
    }

    unlockInputs();
    return error();
}
int
SOP_PrimGroupCentroid::bindToCentroids(fpreal t, int mode, int method)
{
    int                         behavior;
    exint                       int_value;

    const GA_PrimitiveGroup     *group;
    GA_PrimitiveGroup           *all_prims, *temp_group;
    GA_Range                    pr_range;
    GA_ROAttributeRef           attr_gah, primattr_gah;
    GA_ROHandleI                class_h;
    GA_ROHandleS                str_h;

    const GU_Detail             *input_geo;

    UT_Matrix4                  mat;
    UT_String                   attr_name, pattern, str_value;
    UT_Vector3                  pos;

    // Get the second input geometry as read only.
    GU_DetailHandleAutoReadLock gdl(inputGeoHandle(1));
    input_geo = gdl.getGdp();

    // Get the unmatched geometry behavior.
    behavior = BEHAVIOR(t);

    // Create a new attribute reference map.
    GA_AttributeRefMap          hmap(*gdp, input_geo);

    // Get the attribute selection string.
    BIND(pattern, t);

    // If we have a pattern, try to build the ref map.
    if (pattern.length() > 0)
        buildRefMap(hmap, pattern, gdp, input_geo, mode, GA_ATTRIB_POINT);

    // The list of GA_Primitives in the input geometry.
    const GA_PrimitiveList &prim_list = gdp->getPrimitiveList();

    // Create a temporary primitive group so we can keep track of all the
    // primitives we have modified.
    all_prims = createAdhocPrimGroup(*gdp, "allprims");

    // Determine which attribute we need from the points, based on the mode.
    switch (mode)
    {
        case 0:
            attr_name = "group";
            break;
        case 1:
            attr_name = "name";
            break;
        case 2:
            attr_name = "class";
            break;
        default:
            addError(SOP_MESSAGE, "Invalid mode setting");
            return 1;
    }

    // Find the attribute.
    attr_gah = input_geo->findPointAttribute(attr_name);

    // If there is no attribute, add an error message and quit.
    if (attr_gah.isInvalid())
    {
        addError(SOP_ATTRIBUTE_INVALID, attr_name);
        return 1;
    }

    // If not using groups, we need to check if the matching primitive
    // attribute exists on the geometry.
    if (mode != 0)
    {
        // Try to find the attribute.
        primattr_gah = gdp->findPrimitiveAttribute(attr_name);

        // If there is no attribute, add an error message and quit.
        if (primattr_gah.isInvalid())
        {
            addError(SOP_ATTRIBUTE_INVALID, attr_name);
            return 1;
        }
    }

    // 'class' uses the int handle.
    if (mode == 2)
        class_h.bind(attr_gah.getAttribute());
    // Groups and 'name' use the string handle.
    else
        str_h.bind(attr_gah.getAttribute());

    for (GA_Iterator it(input_geo->getPointRange()); !it.atEnd(); ++it)
    {
        if (mode == 0)
        {
            // Get the unique string value.
            str_value = str_h.get(*it);

            // Find the group on the geometry to bind.
            group = gdp->findPrimitiveGroup(str_value);

            // Ignore non-existent groups.
            if (!group)
                continue;

            // Skip emptry groups.
            if (group->isEmpty())
                continue;

            // The primtives in the group.
            pr_range = gdp->getPrimitiveRange(group);
        }
        else
        {
            if (mode == 1)
            {
                // Get the unique string value.
                str_value = str_h.get(*it);
                // Get the prims with that string value.
                pr_range = gdp->getRangeByValue(primattr_gah, str_value);
            }
            else
            {
                // Get the unique integer value.
                int_value = class_h.get(*it);
                // Get the prims with that integery value.
                pr_range = gdp->getRangeByValue(primattr_gah, int_value);
            }
            // Create an adhoc group.
            temp_group = createAdhocPrimGroup(*gdp);
            temp_group->addRange(pr_range);
        }

        // Add the primitives in the range to the groups.
        all_prims->addRange(pr_range);

        // Bounding Box
        if (method == 1)
        {
            // Calculate the bouding box center for this range.
            boundingBox(gdp, pr_range, prim_list, pos);
        }
        // Center of Mass
        else if (method == 2)
        {
            // Calculate the center of mass for this attribute value.
            centerOfMass(pr_range, prim_list, pos);
        }
        // Barycenter
        else
        {
            // Calculate the barycenter for this attribute value.
            baryCenter(gdp, pr_range, prim_list, pos);
        }

        // Build the transform from the point information.
        buildTransform(mat, input_geo, pos, *it);

        // Transform the geometry from the centroid.
        if (mode == 0)
            gdp->transform(mat, group);
        else
            gdp->transform(mat, temp_group);

        // Copy any necessary attributes from the incoming points to the
        // geometry.
        if (hmap.entries())
        {
            for (GA_Iterator pr_it(pr_range); !pr_it.atEnd(); ++pr_it)
            {
                hmap.copyValue(GA_ATTRIB_PRIMITIVE,
                               *pr_it,
                               GA_ATTRIB_POINT,
                               *it);
            }
        }
    }

    // We want to destroy prims that didn't have a matching name/group.
    if (behavior)
    {
        // Flip the membership of all the prims that we did see.
        all_prims->toggleEntries();

        // Destroy the ones that we didn't.
        gdp->deletePrimitives(*all_prims, true);
    }

    return 0;
}
OP_ERROR
SOP_PrimCentroid::cookMySop(OP_Context &context)
{
    fpreal                      now;
    int                         method;

    const GA_Attribute          *source_attr;
    const GA_AttributeDict      *dict;
    GA_AttributeDict::iterator  a_it;
    GA_Offset                   ptOff;
    GA_RWAttributeRef           n_gah;
    GA_RWHandleV3               n_h;

    const GEO_Primitive         *prim;

    const GU_Detail             *input_geo;

    UT_BoundingBox              bbox;
    UT_String                   pattern, attr_name;
    UT_WorkArgs                 tokens;

    now = context.getTime();

    if (lockInputs(context) >= UT_ERROR_ABORT)
        return error();

    // Clear out any previous data.
    gdp->clearAndDestroy();

    // Get the input geometry as read only.
    GU_DetailHandleAutoReadLock gdl(inputGeoHandle(0));
    input_geo = gdl.getGdp();

    // Find out which calculation method we are attempting.
    method = METHOD(now);

    // Create the standard point normal (N) attribute.
    n_gah = gdp->addNormalAttribute(GA_ATTRIB_POINT);

    // Bind a read/write attribute handle to the normal attribute.
    n_h.bind(n_gah.getAttribute());

    // Construct an attribute reference map to map attributes.
    GA_AttributeRefMap hmap(*gdp, input_geo);

    // Get the attribute selection string.
    ATTRIBUTES(pattern, now);

    // Make sure we entered something.
    if (pattern.length() > 0)
    {
        // Tokenize the pattern.
        pattern.tokenize(tokens, " ");

        // The primitive attributes on the incoming geometry.
        dict = &input_geo->primitiveAttribs();

        // Iterate over all the primitive attributes.
        for (a_it=dict->begin(GA_SCOPE_PUBLIC); !a_it.atEnd(); ++a_it)
        {
            // The current attribute.
            source_attr = a_it.attrib();

            // Get the attribute name.
            attr_name = source_attr->getName();

            // If the name doesn't match our pattern, skip it.
            if (!attr_name.matchPattern(tokens))
                continue;

            // Create a new point attribute on the current geometry
            // that is the same as the source attribute.  Append it and
            // the source to the map.
            hmap.append(gdp->addPointAttrib(source_attr).getAttribute(),
                        source_attr);
        }

        // Copy local variables.
        if (COPY(now))
        {
            // Traverse the variable names on the input geometry and attempt to
            // copy any that match to our new geometry.
            input_geo->traverseVariableNames(
                SOP_PrimCentroid::copyLocalVariables,
                gdp
            );
        }
    }

    // Get the list of input primitives.
    const GA_PrimitiveList &prim_list = input_geo->getPrimitiveList();

    // Add points for each primitive.
    ptOff = gdp->appendPointBlock(input_geo->getNumPrimitives());

    // Iterate over primitives using pages.
    for (GA_Iterator it(input_geo->getPrimitiveRange()); !it.atEnd(); ++it)
    {
        // Get the primitive from the list.
        prim = (const GEO_Primitive *) prim_list.get(*it);

        if (method)
        {
            // Get the bounding box for the primitive and set the point's
            // position to be the center of the box.
            prim->getBBox(&bbox);
            gdp->setPos3(ptOff, bbox.center());
        }
        else
            // Set the point's position to be the bary center of the primitive
            gdp->setPos3(ptOff, prim->baryCenter());

        // Set the point's normal to be the normal of the primitive.
        n_h.set(ptOff, prim->computeNormal());

        // If we are copying attributes, copy the primitive attributes from
        // the current primitive to the new point.
        if (hmap.entries() > 0)
            hmap.copyValue(GA_ATTRIB_POINT, ptOff, GA_ATTRIB_PRIMITIVE, *it);

        // Increment the point offset.
        ptOff++;
    }

    unlockInputs();
    return error();
}
int
SOP_PrimGroupCentroid::buildCentroids(fpreal t, int mode, int method)
{
    bool                        store;
    exint                       int_value;

    const GA_AIFStringTuple     *ident_t;
    GA_Attribute                *ident_attrib;
    GA_Offset                   ptOff;
    GA_RWAttributeRef           ident_gah;
    GA_RWHandleI                class_h;

    const GU_Detail             *input_geo;

    UT_BoundingBox              bbox;
    UT_String                   attr_name, pattern, str_value;
    UT_Vector3                  pos;

    UT_Array<GA_Range>          range_array;
    UT_Array<GA_Range>::const_iterator  array_it;
    UT_StringArray              string_values;
    UT_IntArray                 int_values;

    // Get the input geometry as read only.
    GU_DetailHandleAutoReadLock gdl(inputGeoHandle(0));
    input_geo = gdl.getGdp();

    // Check to see if we should store the source group/attribute name as an
    // attribute the generated points.
    store = STORE(t);

    // If we want to we need to create the attributes.
    if (store)
    {
        // A 'class' operation, so create a new integer attribute.
        if (mode == 2)
        {
            // Add the int tuple.
            ident_gah = gdp->addIntTuple(GA_ATTRIB_POINT, "class", 1);
            // Bind the handle.
            class_h.bind(ident_gah.getAttribute());
        }
        // Using the 'name' attribute or groups, so create a new string
        // attribute.
        else
        {
            attr_name = (mode == 0) ? "group" : "name";

            // Create a new string attribute.
            ident_gah = gdp->addStringTuple(GA_ATTRIB_POINT, attr_name, 1);
            ident_attrib = ident_gah.getAttribute();

            // Get the string tuple so we can set values.
            ident_t = ident_gah.getAIFStringTuple();
        }
    }

    // Create a new attribute reference map.
    GA_AttributeRefMap          hmap(*gdp, input_geo);

    // Get the attribute selection string.
    ATTRIBUTES(pattern, t);

    // If we have a pattern, try to build the ref map.
    if (pattern.length() > 0)
        buildRefMap(hmap, pattern, gdp, input_geo, mode, GA_ATTRIB_PRIMITIVE);

    // The list of GA_Primitives in the input geometry.
    const GA_PrimitiveList &prim_list = input_geo->getPrimitiveList();

    // Creating by groups.
    if (mode == 0)
    {
        // Get the group pattern.
        GROUP(pattern, t);

        // If the group string is empty, get out of here.
        if (pattern.length() == 0)
            return 1;

        buildGroupData(pattern, input_geo, range_array, string_values);
    }
    // 'name' or 'class'.
    else
    {
        // Build the data.  If something failed, return that we had an issue.
        if (buildAttribData(mode, input_geo, range_array, string_values, int_values))
            return 1;
    }

    // Iterate over each of the primitive ranges we found.
    for (array_it=range_array.begin(); !array_it.atEnd(); ++array_it)
    {
        // Create a new point.
        ptOff = gdp->appendPointOffset();

        // Bounding Box
        if (method == 1)
        {
            // Calculate the bouding box center for this range.
            boundingBox(input_geo, *array_it, prim_list, pos);
            // Set the point's position to the center of the box.
            gdp->setPos3(ptOff, pos);
        }
        // Center of Mass
        else if (method == 2)
        {
            // Calculate the center of mass for this range.
            centerOfMass(*array_it, prim_list, pos);
            // Set the point's position to the center of mass.
            gdp->setPos3(ptOff, pos);
        }
        // Barycenter
        else
        {
            // Calculate the barycenter for this range.
            baryCenter(input_geo, *array_it, prim_list, pos);
            // Set the point's position to the barycenter.
            gdp->setPos3(ptOff, pos);
        }

        // Store the source value if required.
        if (store)
        {
            // 'class', so get the integer value at this iterator index.
            if (mode == 2)
            {
                int_value = int_values(array_it.index());
                class_h.set(ptOff, int_value);
            }
            // 'name' or by group, so get the string value at this iterator
            // index.
            else
            {
                str_value = string_values(array_it.index());
                ident_t->setString(ident_attrib, ptOff, str_value, 0);
            }
        }

        // If there are no entries in the map then we don't need to copy
        // anything.
        if (hmap.entries() > 0)
        {
            GA_WeightedSum              sum;

            // Start a weighted sum for the range.
            hmap.startSum(sum, GA_ATTRIB_POINT, ptOff);

            // Add the values for each primitive to the sum.
            for (GA_Iterator it(*array_it); !it.atEnd(); ++it)
            {
                hmap.addSumValue(sum,
                                 GA_ATTRIB_POINT,
                                 ptOff,
                                 GA_ATTRIB_PRIMITIVE,
                                 *it,
                                 1);
            }

            // Finish the sum, normalizing the values.
            hmap.finishSum(sum,
                           GA_ATTRIB_POINT,
                           ptOff,
                           1.0/(*array_it).getEntries());
        }
    }

    return 0;
}