Below are a number of useful suggestions for common tasks.
To determine the position of a CV in an uninstanced piece of geometry simply use the worldPosition() methods on the various CV classes.
However, when geometry is instanced it is not possible to use the worldPosition() methods (since they will only indicate the position of the first instance). Instead it is necessary to use the affectedPosition() methods. Use the method AlDagNode::globalTransformation() to calculate the transformation matrix of the instance node. Use this AlTM and the CV's affectedPosition() method to get the position of the CV in the instanced geometry.
Sometimes it is desirable to get the position of a CV including the effect of any clusters but excluding the DAG transformations above the CV. To do this supply the affectedPosition() methods with an identity matrix.
This method is only useful to OpenAlias plug-ins. Any time a change has been made to a model or a window this method should be called. However it could be expensive to use so a better strategy might be to only use it after completing all necessary changes. For example, a plug-in may project several curves on to a surface and then trim the surface. AlUniverse::redrawScreen() could be called after each projection and after the trim - this would create an interesting visual effect as each curve appears on the surface. Alternatively if the visual effect is not necessary, and speed is of greater importance, then AlUniverse::redrawScreen() need be called only after the trim operation is complete.
Note that not using AlUniverse::redrawScreen() will cause the display to show out-of-date images of the data.
Performance can be improved by postponing screen updates until all objects have been updated. This speedup can be implemented by the developer using AlUniverse::doUpdates().
An iterator is a special form of a callback function. By providing an overloaded class, you can also maintain data between each call to the callback function.
When using iterators to find AlObjects it is necessary to copy the wrapper rather than just storing a pointer to it. Below is an example of an iterator which looks for a DAG node named George.
class findGeorgeIterator : public {
public:
findGeorgeIterator() : george( NULL ) {};
~findGeorgeIterator() {};
virtual int func( AlObject* obj )
{
if ( strcmp( obj->name(), "George" ) )
{
george = obj->copyWrapper(); // Copy the wrapper!
return 1;
}
};
AlDagNode* result() { return george; };
private:
AlDagNode* george;
}
// ... then to use the class.
int result;
findGeorgeIterator iter;
AlDagNode *george = NULL;
if ( sSuccess == AlUniverse::applyIteratorToDagNodes(
&iter, result ) )
{
george = iter.result();
}
If the result of such an iterator is always used then the above is adequate. However, if it is possible that the return value could be ignored, a better implementation of the iterator would have the destructor delete the wrapper and have the result method return a copy of the saved wrapper.
If you forget to delete a wrapper or lose a pointer to one, you can use AlDebug::cleanUpUniverse(). This method deletes all invalid wrapper objects in the current universe. Wrapper operations (most notibly the deleteObject() operations) will slow down with the more wrappers you use. If calls to the API deleteObject() method start to slow down the longer it runs, it is likely that wrappers are being lost (resulting in a memory leak) and the code should be corrected. As a temporary kludge, the AlDebug::cleanupUniverse() method can be called.
It should be noted though that AlDebug::cleanUpUniverse() should be avoided if it all possible. It could cause core dumps if used when other plug-ins are running or with wrappers that are not dynamically allocated. (See the AlDebug::cleanUpUniverse() documentation for a discussion of why.) Furthermore, OpenAlias developers should note that this function deletes all the wrappers in the universe, regardless of the plug-in that created them. This could cause another plug-in to lose a reference to data and crash as a result.
In short, this function is intended for debugging purposes until you have eliminated all of your memory leaks.
As mentioned earlier wrappers may be invalidated. A wrapper references Alias data. When the Alias data is deleted, rather than deleting the wrapper we just invalidate it. (If we were to delete it an API user might try to dereference it and would cause a core dump.) An invalidated wrapper is one which does not reference any Alias data. Accessing any of the methods of an invalid wrapper will not cause a fatal error, but will cause a failure code (usually sInvalidObject) or an error value to be returned. The function AlIsValid() may be used on objects derived from AlObject to determine if they are valid or not. AlIsValid() will return FALSE for an input of NULL.
In OpenAlias and OpenModel when moving between stages, wrappers which reference objects in inactive stages become invalid until the stage becomes active again. They will also be made invalid if the universe they are in is deleted.
The destructor deletes the wrapper and leaves the Alias data intact. To delete the Alias data use the wrapper's deleteObject() method which will delete the object and invalidate the wrapper. Note that although every class derived from AlObject has a deleteObject() method, not every type of Alias data can be deleted from a plug-in. For instance, curve CVs are not deletable. As a result, calling deleteObject() on an AlCurveCV will simply return sFailure.
The copyWrapper() method allows developers to create a new wrapper which references the same Alias data as an existing wrapper. As a result it is not possible to compare wrapper pointers to learn whether they reference the same object, instead you must use AlAreEqual() to determine whether to wrappers reference the same object.
To accomplish some tasks the universe relies on messages. The API provides methods to receive and post messages. It is also possible to define a custom message type which extends the standard collection of Alias messages. Although Alias has no understanding of the new message type (i.e., Alias cannot receive messages from plug-ins or OpenModel applications), plug-ins can use them as communication channels. This allows for interesting possibilities, such as a group of plug-ins collaborating on a function. See the documentation on the AlMessage class for more information.
The AlIterator class simplifies writing code that must operate on lists of objects. AlIterators significantly reduce the overhead associated with using wrappers to walk lists of items. The applyIterator() methods on many classes will traverse a list of objects in the most efficient method possible and may reuse wrappers. For example, using AlSet::firstMember() and AlSetMember::nextSetMember() may require O(nlog n) operations to traverse the entire Alias set. Using the iterator requires only O(n) operations. See the documentation in the AlIterator class.
Explicit traversals can be optimized through the use of the `nextD' and `prevD' destructive methods.
AlCurveCV* cv;
if ( cv = curve->firstCV() )
{
do
{ ... do something ... }
while( sSuccess == cv->nextD());
delete cv;
}
The statement cv->nextD() causes the current wrapper to reference the next CV in the curve. No additional memory is allocated, and in general the destructive methods will run faster than the non-destructive methods.
Whenever possible, one should use the AlIterator class when traversing a collection of objects. Not only will the traversal be as quick as possible, but the memory management is taken care of automatically. The wrappers passed to the iterator's callback function are destroyed by the class and need not be freed explicitly.
The class AlPickList provides access to the list of active objects from within an OpenAlias plug-in and OpenModel applications. The pick list could include objects picked from the modeling windows and the multilister. AlPickList also includes methods to manipulate the pick list and perform global picking operations, such as picking an object from the screen. Most of the non-interactive methods will also function in OpenModel, making this class a useful utility in both situations.
Many objects are derived from AlPickable. Like AlAnimatable and AlClusterable, AlPickable encapsulates the shared behavior of a collection of types, in this case the behavior of all objects that can be picked. See the AlPickable documentation for further information.
Setting the direction of a light can be accomplished using the setDirection() method in the AlDirectionLight and AlSpotLight classes.
The API supports creating and deleting keyframes in blocks. In the class AlParamAction, there are two methods called addKeyframeRange() and deleteKeyframeRange() for performing block keyframe creation and deletion. These block operations will be faster than individual creation or deletion of keyframes.
Alias and the API have slightly different definitions of curve and surface form. After building a Closed curve or surface in Alias where the first and last CV's are co-incident, the information window will still report that the curve or surface is Open. This is because Alias does not support the concept of Closed geometry. The API supports Closed geometry and AlCurve::form() and AlSurface::vForm() or uForm() will report that the curve or surface is Closed.
To support more efficient use of polysets and polygons, use the batch creation and deletion routines in these classes.
The AlEscapeKeyPressed() function defined in AlLiveData.h returns TRUE if the Escape key has been pressed. When going into loop operations that may take a great deal of time to complete, add a call to this function. If the user has pressed Escape, cleanup and exit the loop. This function only exists in OpenAlias.
Two simple coding practices can make a plug-in or OpenModel application run faster. These ideas are stated elsewhere in the API documentation but are worth repeating here. Adhering to these coding practices are even more important if your code will be dealing with large numbers of objects.
An additional coding practice that can be utilized is wherever possible take advantage of methods that perform operations on multiple objects. Some of the batch routines are described above. These include creating and deleting keyframes, polygons and vertices.