Autodesk



Your Ad Here


Alias Programmers' Interfaces (API) Plug-in API Examples > Option box scheme example

Alias Programmers' Interfaces (API)

Describes the APIs to allow programs to interact with and extend AliasStudio.

Introduction

Describes the API, what it can and can't do, and who should use it.

What is the API?

The API provides developers with access to AliasStudio internal data and functionality through two products, OpenModel and OpenAlias.

The API is written in C++ and is supplied as C++ classes. Most of the discussion in this document is intended for C++ programmers, and we assume that you understand the C++ concepts of classes and inheritance, and have a familiarity with the AliasStudio interactive software.

The components of the API are:

What Can the API Do?

A brief list of some of the possibilities includes

Plug-ins can be written using OpenAlias, which can read or write new file formats and can interactively generate geometry, lights, and shaders. Many stand-alone OpenModel applications can be easily converted into OpenAlias plug-ins.

What Can't the API Do?

The API does not give access to all functions in AliasStudio. You do not have access to functions such as the Rail surface tool. However, with each release we provide more functionality to the API programmer.

Who needs OpenModel?

The AliasStudio wire file format is a binary format and is therefore not modifiable without proprietary knowledge of AliasStudio data structures.

We have therefore provided a library that insulates developers from the details of the file format and from any future changes in the file format. Since a library is available to read, write, and query wire files, developers do not need to make software changes to accommodate wire file changes. Re-linking with a new wire file library is sufficient.

With this library, you can:

Who Needs OpenAlias?

If C++ developers want to interact with data internal to the interactive AliasStudio package, then OpenAlias is for them. With OpenAlias it is possible to:

OpenModel versus OpenAlias

Deciding whether to use OpenModel or OpenAlias is largely a simple matter of deciding whether instantaneous response is a concern. OpenModel allows for the development of stand-alone applications which can operate on several files one after another or can be launched from a script, and therefore require little interaction with a user.

OpenAlias, on the other hand, is for developers who need instant feedback or wish to integrate new custom features into AliasStudio. The results of using a plug-in appear immediately on the screen.

Still unsure? If so, then it is likely best to start developing plug-ins. OpenAlias generally provides a tighter integration with AliasStudio providing a greater number of possible solutions to design decisions.

Where do I start?

Within the API there is an object called the "Universe", which contains all geometry, lights, cameras and clusters for a model, as well as animation and rendering data. Developers can use the API to change the universe and its contents.

Understanding the AliasStudio universe and the class hierarchy is the best way to get started.

How to Use this Document

The body of this document assumes a knowledge of the AliasStudio interactive software. Keep the AliasStudio manual at hand for any unfamiliar terms.

Documentation Notes

In the documentation, the characters "<", ">" and "|" are used to indicate how an argument is used by a method.

Note to Windows users

Occasionally, there are places in the document where you will encounter UNIX-style directory path names. Keep this in mind when you see path names with forward slashes.

The universe and its objects

Describes the basic concepts and procedures you need to understand.

The Universe

The universe is a static class that can access all objects in a wire file or within AliasStudio. The main feature of the universe is that it has a pointer to a tree-like data structure which is called a directed acyclic graph (DAG). All geometry, light, camera and cluster information is stored in this DAG. The DAG is composed of nodes called DAG nodes.

This diagram illustrates an example model. Arrows indicate that a class references the class that it is pointing to. Double sided arrows indicate a two way reference.

The topmost DAG node is called the "world". In this example, the world node (returned by AlUniverse::firstDagNode()) has three group nodes as its "children" and an AlFaceNode as a sibling. Notice that the two group nodes below the topmost DAG node are instances. They both refer to the same curve node. NULL nodes (with nothing below them) created in the AliasStudio interactive package are retrieved as AlDagNodes.

There are several classes derived from the DAG node class that refer to specific objects that contain geometry, light, camera, or cluster information. (When a DAG node "refers" to an object, it means that the DAG node has the object as a child object.) for example, a camera node is a DAG node that refers only to a camera object. A light node is a DAG node that refers only to a light object. Combinations such as these tend to be created at the same time, manipulated together, and deleted at the same time. Whenever a DAG node is created, it is inserted into the world position. After creating a DAG node, the method AlUniverse::firstDagNode() will return this node. Other creation routines (such as texture creation) can also insert nodes into the world position as the first node.

Cameras

Above, we see three camera nodes and their camera object. The nodes represent the camera's eye, view, and up positions. To create this combination, it would be necessary to instantiate and create an AlOrthographicCamera or an AlPerspectiveCamera. When they are first created, the camera nodes are siblings under an AlGroupNode.

Lights

This diagram shows a light and its three light DAG nodes, grouped under a common group node. To create this structure, instantiate and create one of the several different light types. The three light nodes (the position, look at, and up DAG nodes) are created. The three light DAG nodes are grouped under a common group node. All of the different light types make use of the position DAG node; however, only the AlSpotLight makes use of the look at and up nodes.

Curves and Faces

Some objects have child objects as well. For example the AlCurve class has AlCurveCV as a child class.

Here we see a curve DAG node, its curve object, and control vertices (CVs). To create this entire structure, first instantiate and create an AlCurve, then instantiate and create an AlCurveNode object. Note that if an AlCurve is created and not associated with an AlCurveNode, it doesn't become part of the universe. In this case, if the pointer to the AlCurve is lost, the AlCurve becomes wasted memory.

A face is similar to a curve. Here we see a face DAG node, its face objects and their control vertices (CVs). A face must have at least one face object. To create a face, instantiate and create an AlFace then instantiate an AlFaceNode and pass the AlFace to the face node's create method. As with curves, if the pointer to an AlFace is lost without first being associated with an AlFaceNode, the AlFace becomes wasted memory.

Surfaces

Surfaces, like curves, have child classes, but surfaces are far more complex and have a far greater number of child classes, including trim classes. There are also surface methods which allow you to project a curve onto the surface, create a curve-on-surface, and other methods which allow you to trim the surface using the curves on the surface.

Above is a surface node, its surface object and associated objects for control vertices (CVs) and curve-on-surface's. To create a surface/surface node pair, first instantiate and create an AlSurface, then instantiate and create a surface node. The surface CVs are created automatically. To create a curve-on-surface, instantiate and set the data of an AlCurveOnSurface object, then add it to an AlSurface. While you are able to add rows of CVs you are not able to delete CVs. Make sure you create a surface node for each surface that you create; a surface without a surface node is not added to the universe, and if the pointer is lost, the surface would become wasted memory.

Clusters

Some objects rather than having a child object, refer to other objects in the universe. In particular the AlSetMember and AlClusterMember classes refer to objects which are components of the set or cluster respectively.

Here we have a cluster DAG node, its cluster object and cluster members. To create this structure, instantiate and create an AlCluster object. It is not possible to create or delete individual AlClusterMember objects. Instead, they are created or deleted when objects are added to or removed from the cluster.

Sets

This diagram shows some sets and their set members. A set has no DAG node and is not in the DAG; to get the global list of sets, use the AlUniverse::firstSet() method. To create a set, instantiate and create an AlSet and add members to the set. As with cluster members it is not possible to create or delete individual AlSetMember objects instead, they are created when objects are added to or removed from the set.

Animation and Rendering

Animation and rendering information can also be accessed through the universe. For example, you can traverse the list of all the channels, actions,or shaders in the universe. However, this information is also accessible through the objects that use it. For example, surfaces and faces have access to the list of shaders and animated objects that they use - for example, a CV, light or texture has access to the channels that animate it. The diagrams below show an animation and rendering example respectively of the relationship between objects that refer to each other. In these figures, all objects are represented in a rectangular box.

This diagram shows a CV on a surface animated by a motion path and timing curve. To create this structure: instantiate and create an AlMotionAction using an AlCurveNode above the motion path curve; instantiate and create an AlChannel with the AlSurfaceCV to animate, the X, Y, or Z parameter of a CV; and the newly-created AlMotionAction; now use the applyWarp() method of the AlChannel, which creates the AlParamAction as a timing curve. Access the two AlKeyframes through the AlParamAction and change the timing of the animation by using the setLocation() method of AlKeyframe.

Shaders and Textures

This diagram shows a surface with two shaders, the first shader having two textures. To create this structure, first create a surface. Then create the two shaders with AlShader::create(). To one of them add textures with AlShader::addTexture(). Then use AlSurface::assignShader()to add the first shaders to the surface and AlSurface::layerShader() to add the second one.

Implementation Details

Describes coding concepts unique to the API.

Class Hierarchy

All but a few objects in the API are derived from a base class called "AlObject". By deriving all object classes from a common base class, most things can be treated as AlObjects until you need to know otherwise.

To find the type of an AlObject, you can query the type or use the safe type-casting methods described later in this document.

Wrappers

The objects which make up the API (C++ classes) are wrappers on internal AliasStudio data, whether read in from a file, or created while running the interactive package. These wrappers can be thought of as a means to indirectly access the data. Access to the data is done through methods contained in the wrapper classes. This provides a clean and consistent view of the data and hides its internal representation.

Wrappers are only allocated when necessary. This means that developers using the API are responsible for deleting wrappers when they are no longer needed.

New versus Reused Wrappers

All methods which return a pointer to an object return a new wrapper, except for the casting methods (i.e. AlObject::asLightNodePtr()). Methods are provided to reuse wrappers when sets of data are processed as a group. These include any method whose name ends in "D" (that is, AlSurface::nextShaderD()), and any iterator (i.e. AlIterator).

Instantiation and Creation

The API separates the operations of instantiation and creation. Developers must initialize the universe before doing anything else. Since most objects are derived from the AlObject base class, safe type casting must be used to convert a base object to a derived object. Deleting objects and wrappers requires extra care.

The creation of an object often involves both instantiation and initialization. The initialization part, if it's simple, can be put in the constructor of a class. However, if the initialization involves an operation that could fail (for example, memory allocation could fail), then the constructor is inadequate to convey failure. To address this, the API uses "create" methods to perform initialization because they return useful status codes. The following is an example of how an object should be created, and returns NULL if creation was unsuccessful.

// instantiation
AlAmbientLight *ambientLight = new AlAmbientLight;
if (ambientLight == NULL) {
    return NULL;
}
// creation
statusCode status = ambientLight->create();
switch(status) {
    case sSuccess: 
      		break;
    	case sInsufficientMemory:
    	default:
		     delete ambientLight; // Remember to free the 
memory
     		return NULL;
} 

Safe Type-casting

Type-casting a pointer from a derived class to a base class is inherent to C++. Type-casting a pointer from a base class to a derived class is not safe and could possibly point to the incorrect data. The API provides methods to achieve this "down-casting". The example programs, especially the DAG traversal functions, show how down-casting is used.

For example, if class derivation is AlObject->AlLight->AlAmbientLight, where the AlObject class is the base class, and AlAmbientLight is the most derived class, the following code shows how down-casting is done:

AlAmbientLight *ambientLightPtr = objectPtr->asAmbientLightPtr();
if (ambientLightPtr != NULL) {
	// then we know the object is an ambient light
} 

You do not have to first cast the object to an AlLight. Also note that a new wrapper is not allocated when using the casting methods. In this way these methods can be thought of as regular casts.

Deletion

Be very careful when deleting objects from the universe.

Some complex objects are closely linked and behave as a single entity. This includes objects such as camera nodes and camera objects.When a member of the group is deleted, the other members will also be deleted.

For example, when one of the three camera nodes is deleted, the other two nodes will also be deleted. Alternatively, if a camera node is deleted, its camera (and other camera nodes) are deleted. Since the camera nodes are often siblings, developers should be careful that they are not left with a pointer that refers to a deleted object.

An advantage to wrappers is that they can be invalidated, meaning that the internal object they reference may be deleted, but the wrapper remains with the knowledge that it is not valid. Methods of an invalid wrapper will fail gracefully, avoiding dereferencing dangling pointers.

Core dumps

If a core dump happens, it could mean one of the following:

Comparing Pointers

It is not possible to compare wrapper pointers to see if the wrapper objects are the same. Any number of different wrapper objects may reference the same AliasStudio data internally. It is necessary to use AlAreEqual() to compare objects. This will compare the AliasStudio objects to which the wrappers point. It is a true test of equality between AliasStudio objects.

In addition, checking a pointer for NULL is not sufficient for determining its validity. It is possible that through interaction with AliasStudio, a developer may delete an object, causing any associated wrappers to become invalid, lest they point to freed memory. AlIsValid() should be used on wrappers that may have become invalidated since they were last referenced. All classes implicitly perform validity checks on instances, so a member function called on an invalid object will simply fail.

Allocation of Input Values

As a rule, the API developer is not responsible for allocation of arguments to functions.

For example, if a function takes a string as an argument, that string will be duplicated by the API. The developer may then deallocate the string. The exceptions to this rule are the old blind data interface (user defined and only accessible through the API) and comments. Blind data is not copied on input. Rather, a pointer to the user's buffer is kept in the AliasStudio object. As long as a reference exists from AliasStudio to that blind data, the developer should avoid deleting the associated buffer. Note that when the wire file is stored, the blind data is copied into the wire file. When a wire file is retrieved, any blind data will then be copied into a newly allocated buffer. This has the same effect as creating duplicates of any shared blind data. As a general rule it is not a good idea to allow objects to share blind data.

Blind Data and New and Delete

Using the C++ new and delete functions to create and destroy memory for blind data can lead to program errors. There are several cases where AliasStudio or OpenModel will need to remove or re-create blind data during normal operations. If a delete all operation is performed, all blind data is removed. If blind data is saved in the wire file, when the file is read in the blind data storage will be recreated. OpenAlias and OpenModel will use malloc() and free() for its blind data memory creation and destruction routines. Problems may occur if the blind data has been created with the new command since the wrong creation and destruction routine for the memory block would be used. As a general rule malloc() should be used to allocate memory used for blind data or comments.

There are currently two ways of managing blind data within the API. You can use the old blind data methods(setBlindData(), blindData(), removeBlindData()) or use the new AlBlindData class. The rules above apply to the old blind data methods rather than the AlBlindData class.

Allocation of Return Values

There has been some confusion in the past over which functions allocate their return values and which functions return pointers to existing data. As of the Version 7 release, we have taken care to keep the treatment of return values consistent. For example, all functions that return strings now return "const char *" references to AliasStudio data. We believe this approach to be more general--it does not force the developer to perform extra memory management if it is not necessary. If this returned string is required for future reference, then it is up to the developer to make a copy.

A Few Simple Rules for Return Values

While the allocation of return values may change and although there may be some exceptional cases, here are some rules of thumb to remember.

It is important to remember that return values will point to internal AliasStudio objects that may change. For example, the return of an AliasStudio object's name may change due to an action performed in the interactive package.

Reserved Types for Blind Data

Contact Autodesk if you would like to have a block of user-types reserved for your plug-in applications.

Limitations

Compiling and linking

Describes the process of creating a working executable application that uses the API.

Files and Directories

In the AliasStudio directory, there is a directory called ODS (an abbreviation for Open Digital Studio):

Inside the directory are three directories relevant to the API:

Compiling code for either OpenModel or OpenAlias is straightforward and the process can easily be understood when reading the example Makefiles in /usr/aw/alias/ODS/OpenModel (or ODS/OpenAlias). However below are detailed the important concepts.

Setting the Include Path

When building API programs, ensure that the include path for the compiler includes the path (in the CFLAGS variable):

-I$(ALIAS_LOCATION)/ODS/Common/include

where $(ALIAS_LOCATION) is usually /usr/aw/alias. The definition of ALIAS_LOCATION at the top of the example Makefile is only necessary if you need to override the value in the environment variable.

On Windows, the CFLAGS and CPLUSPLUSFLAGS variable would include the following:

/I$(ALIAS_LOCATION)\ODS\Common\include

Windows development environment requirements

Microsoft Visual Studio .NET 2003 is required for developing plug-ins and OpenModel applications on Windows 2000 and XP.

Compiling and linking OpenModel programs for Windows

In the current release, the building of OpenModel applications is done at the command line rather than with in the Microsoft Visual Studio IDE. The OpenModel example applications included are built as console applications for the purpose of writing standard output. For OpenModel, ensure that the library path includes the following:

/LIBPATH:$(ALIAS_LOCATION)/lib

In addition, the LFLAGS variable should contain the following:

/SUBSYSTEM:CONSOLE /NODEFAULTLIB:LIBC.LIB /machine:I386

The link libraries for a console application are:

libalias_api.lib libapi_memory.lib libapi_image.lib \
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib \
advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib \
odbc32.lib odbccp32.lib comctl32.lib netapi32.lib \
version.lib ws2_32.lib

And the CFLAGS and CPLUSPLUSFLAGS variables should contain:

/MD.

The makefiles in the directories OpenModel/examples and OpenAlias/examples demonstrate how all this is done, and you should study them before creating new makefiles.

As well, ensure that Path environment variable is set to reference the AliasStudio library directory along with the directory that contains the compiler libraries. For example, use the following command in Command Prompt Shell:

set Path=%ALIAS_LOCATION%\bin;%SOME_MS_PATH%

Note that the %ALIAS_LOCATION%\bin directory contains the API .dll's and the %ALIAS_LOCATION\lib directory contains the API .lib files required at link time.

Compiling and Linking Plug-ins for Windows

In the current release, the building of OpenAlias plug-ins is done at the command line rather than within the Microsoft Visual Studio IDE.

Linking a plug-in requires the following flags:

/DLL /opt:noref /incremental:no /machine:I386 /NODEFAULTLIB:MSVCRT.lib \ $(ALIAS_LOCATION)\libAliasCore.lib

For example, if the plug-in is composed of the object files obj1.obj obj2.obj obj3.obj, then the plug-in would be linked by the following command:

link.exe /DLL /opt:noref /incremental:no /machine:I386 /NODEFAULTLIB:MSVCRT.lib \ $(ALIAS_LOCATION)\libAliasCore.lib obj1.obj obj2.obj obj3.obj /out:output_name.plugin

Building the included examples

Describes how to build the examples included with the application.

About the API examples

See the Readme files for information on the examples.

OpenAlias/plugins

In this directory you will find precompiled versions of some of the files in the examples directory. We only precompile plug-ins that have useful functionality as opposed to programming examples. Note, however, that these plug-ins, whether compiled or not, are provided as examples only and are not supported by Autodesk.

Common/include

All the include files necessary to build OpenAlias plug-ins are located in this directory.

Common/examples

Some of the example source code is common between OpenAlias and OpenModel. This source code is located in this directory. Note that you will need to have the compiler flag

-I$(ALIAS_LOCATION)/ODS/Common/examples

in your compile flags (CFLAGS) in your Makefile if you use the AlPrint functions in your programs.

Building the API examples on Windows

OpenModel

To build the OpenModel examples, type the following in a command prompt window and exclude any text in brackets:

mkdir examples
cd examples
copy c:\AW\AliasStudio2008\Studio\ODS\OpenModel\examples\Makefile 
.
notepad Makefile         ( Set ALIAS_LOCATION variable to correct 
path. )
vsvars32.bat             ( Set the Visual C++ environment 
variables. )
nmake copy   ( Copies example code into the current directory. )
nmake
set Path=%Path%;c:\AW\AliasStudio2008\Studio\bin (Sets Path 
variable so .dlls can be found
.\cppCreate.exe new.wire     ( Run an example. ) 
> Notes

OpenAlias

To build the OpenAlias examples, type the following in a command prompt and exclude any text in brackets:

mkdir pluginexamples
cd pluginexamples
copy c:\AW\AliasStudio\Studio\ODS\OpenAlias\examples\Makefile .
notepad Makefile         ( Set ALIAS_LOCATION variable to correct 
path. )
vsvars32.bat             ( Set the Visual C++ environment 
variables. )
nmake copy   ( Copies example code into the current directory. )
nmake 

The built plug-ins can now be loaded through the Plug-in Manager.

> Notes

Using the API

Useful suggestions for common tasks.

Sharing Code between OpenModel and OpenAlias

With careful coding, you can share code between OpenModel and OpenAlias plug-ins.

To simplify code sharing, you can:

Determining the Position of an Instance

To determine the position of a CV in an uninstanced piece of geometry simply use the worldPosition() methods on the various CV classes.

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. In the simple case, where instance nodes have no parents there will be distinct paths to the instanced geometry. The method AlDagNode::globalTransformationMatrix() is used 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. In the more complex case of instances being applied to instances, there will be several non unique paths to the geometry. Each path will represent an instanced piece of geometry. It will be necessary to walk each path and accumulate the transformation matrices( while walking down) and then apply that accumulated AlTM to the CV/Vertex positions.

Getting clustered position

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.

Using AlUniverse::redrawScreen

This method is only useful to OpenAlias plug-ins. You should call it whenever a change has been made to a model or a window. However, it could be expensive to use, so you might use it only after completing all necessary changes. For example, a plug-in might project several curves on to a surface and then trim the surface. You could call AlUniverse::redrawScreen() after each projection and after the trim: that would create an interesting visual effect as each curve appears on the surface. Alternatively, if you prefer speed over the visual effects, you might call AlUniverse::redrawScreen() only after trimming is complete.

If you do not use AlUniverse::redrawScreen(), the display shows out-of-date images of the data.

You can improve performance by postponing screen updates until all objects have been updated. The developer can implement this speed-up by using AlUniverse::doUpdates().

Iterating Over Objects

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 AliasStudio 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. The wrappers passed to the iterator's callback function are destroyed by the class and need not be freed explicitly.

Iterators and AlObjects

An iterator is a special form of 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, you need 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, 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.

Cleaning up Lost Wrappers

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 notably 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.

You should avoid AlDebug::cleanUpUniverse() if at all possible. It can 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, this function deletes all the wrappers in the universe, regardless of the plug-in that created them. This can cause another plug-in to lose a reference to data and crash.

In short, this function is intended for debugging until you have eliminated all of your memory leaks.

Invalidating Wrappers

As mentioned earlier, wrappers can be invalidated. A wrapper references AliasStudio data. When the AliasStudio data is deleted, rather than deleting the wrapper we just invalidate it. (If we deleted it, an API user might try to dereference it and cause a core dump.) An invalidated wrapper is one which does not reference any AliasStudio data. Accessing any of the methods of an invalid wrapper does not cause a fatal error, but causes a failure code (usually sInvalidObject) or an error value to be returned.

You can use the function AlIsValid() 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 are also made invalid if the universe that they are in is deleted.

Wrappers, Destructors, and Deleting Objects

The destructor deletes the wrapper and leaves the AliasStudio data intact. To delete the AliasStudio 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 AliasStudio data can be deleted from a plug-in. for example, curve CVs are not deletable. As a result, calling deleteObject() on an AlCurveCV will simply return sFailure.

Copying Wrappers

The copyWrapper() method allows developers to create a new wrapper which references the same AliasStudio 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 the wrappers reference the same object.

How to invoke your OpenModel applications

The application $ALIAS_LOCATION/bin/ print_wire_header now outputs the coordinate system stored in a wire file. You can use this information and a wrapper script to invoke your OpenModel applications with the correct Coordinate axis.

Receiving Messages

To accomplish some tasks the universe relies on messages. The API provides methods to receive and post messages. You can also define a custom message type which extends the standard collection of AliasStudio messages. Although AliasStudio has no understanding of the new message type (because AliasStudio 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.

Picking Objects

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 Light Direction

Setting the direction of a light can be accomplished using the setDirection() method in the AlDirectionLight and AlSpotLight classes.

Creating and Deleting Keyframes

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.

Closed Forms

AliasStudio and the API have slightly different definitions of curve and surface form. After building a Closed curve or surface in AliasStudio 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 AliasStudio 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.

Detecting the Escape Key

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 from the loop. This function only exists in OpenAlias.

Making your code run faster

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 takes advantage of methods that perform operations on multiple objects. Some of the batch routines are described above. These include creating and deleting keyframes.

Blind Data and Byte Ordering

The older blind data interface( blindData(), setBlindData(), removeBlindData() methods ) required special coding if your plug-in makes use of blind data and is intended to run on both the Windows and UNIX platforms. On Windows, the byte ordering is reversed from what it is on UNIX. Program errors will occur if this difference in blind data byte ordering is not handled correctly.

The old interface is now considered deprecated since we now have proper support for platform byte ordering with the new AlBlindData class.

Using view frames

When using the AlViewFrame::viewFrame( ... , const double,Options=kObject) method, it is important to realize that the view will be determined by the heirarchy evaluation that was requested. The Options parameter is one of:

	                kObject 
	                kObjectAndAbove 
	                kObjectAndBelow 
	                kObjectAndAboveBelow 

As a result, you must specify the correct direction of the heirarchy to evaluate. For example, if you were evaluating a leaf node and specified kObjectAndBelow, the view would not change.

Using OpenModel

Information on programming OpenModel.

Dynamic OpenModel Libraries

The OpenModel libraries are supplied as a dynamic shared objects (DSO) ( .sl on HP-UX, .dll on Windows).

Dynamically linking with DSOs requires that the .so libraries be available when the program is run. Using DSOs often decreases the loading time of the application since the libraries are not loaded until they are actually needed. It significantly reduces the size of the application (by over 30 megabytes of space). If multiple copies of the application are run, they can reuse the same library, so only one copy will need to be in memory at once (note: each copy will be run as if it were an entirely separate application, this does not imply that each copy will use the same data). However, you must ensure that:

Using OpenAlias

Information on creating AliasStudio plug-ins with OpenAlias .

Disclaimer

In the directory $ALIAS_LOCATION/ODS/, there are a number of pre-compiled plug-ins and source code examples. These plug-ins and source code examples are provided to you on an "AS-IS" basis. You are free to make use of these plug-ins and source code examples, however Autodesk does not provide any support in connection with them.

Autodesk makes no warranties, express, implied or arising by custom or trade usage, and to the extent permitted by applicable law, specifically disclaims any implied warranties of title, noninfringement or of fitness for a particular purpose. To the extent permitted by applicable law, (I) Autodesk's liability in contract, tort or otherwise arising out of or in connection with the plug-ins or source code shall not exceed the purchase price paid by customer for the plug-ins or source code examples, and (Ii) in no event shall Autodesk be liable for any punitive damages or lost profits or other special, indirect, incidental, or consequential damages, including any damages resulting from loss of business arising out of or in connection with the performance of the plug-ins or source code examples, even if Autodesk has been advised of the possibility of such damages. Customer shall indemnify Autodesk and hold it harmless from and against any loss, claim or damage to any person arising out of customer's use of the plug-ins or source code examples.

Setting up plug-ins

Managing plug-ins

The defaults for OpenAlias are set up such that it is not necessary to do anything before using plug-ins. However, you may want to set up your OpenAlias environment with the configurable options in the AliasStudio Preferences window. These items are both set to appropriate defaults so they do not need to be changed before using plug-ins in AliasStudio. The configurable items are:

Plug-in search path

This colon-delimited path is used to search for plug-ins specified in the auto-load list and by the searching routines of the Plug-in Manager window.

By default this path is

.:$ALIAS_LOCATION/ODS/OpenAlias/plugins

which causes $ALIAS_WORKENV (or the directory AliasStudio was launched from if $ALIAS_WORKENV is not set - AliasStudio does an implicit change of directory to $ALIAS_WORKENV when it starts) to be searched first, followed by the directory containing the example plug-ins. Any number of directories can be listed with each searched in turn until a plug-in is found. Note that this default path is one of many that are searched for plug-ins.

Plug-in message level

The Messages popup allows the selection of Full (verbose) or Brief messages. In Full message mode, detailed information will be written to the errlog file if there is an error during the loading or unloading of plug-ins.

The plug-in manager window

To open the Plug-in manager, choose Utilities > Plug-in Manager Utilities > Plug-in Manager.

Defining a plug-in

A plug-in may consist of several files. The only file required is the C++ source which defines the functionality of the plug-in. Additional files include scheme and icon files. The most common scheme files a plug-in may use are a pair of files describing the option box. One scheme file describes the layout of the option box and the second provides commands which initialize the default values of variables.

Creating custom icons for plug-ins

General Solution

A general solution for creating icons involves using the Studio renderer. A model of the icon you wish to represent would be created within Studio. Use the Render -> Globals -> Image File Output option to set the Format and Depth Format to ALIAS. Then set the X and Y resolution to 32 for small icons or 48 for medium icons. Render the image and then use that image as the icon for the plug-in. This solution will work on any platform. This approach uses a file type 'pix' which is an Alias format

Writing a plug-in

Coding Overview

Building a plug-in requires the creation of a function handle(class AlFunctionHandle). The function handle encapsulates all the operations on a UI menu button. A plug-in can define and control any number of menu buttons. A plug-in must define a entry function called plugin_init(). The exit function called plugin_exit() is optional. Plug-ins without exit functions will not be unloaded.

plugin_init()

This function must be defined as:

extern "C" PLUGINAPI_DECL int plugin_init( const char* 
pluginDirPath ) 

It is run as soon as the plug-in is loaded. pluginDirPath is a NULL-terminated string which contains the absolute pathname to the directory that the plug-in was loaded from. This path is provided as a convenience to the plug-in. It is intended to be passed to the initialization routines as an additional argument. This allows the plug-in to specify the exact location of its scheme and icon files if they are not located in the default directories.

On Windows, PLUGINAPI_DECL is defined to export the associated symbol from the plug-in. The developer uses this function to initialize the OpenAlias universe, and uses instances of the AlFunctionHandle class and AlMomentaryFunction or AlContinuousFunction classes (declared in the file scope to be available to the plugin_exit() function as well) to define the interface between AliasStudio and the plug-in. On successful completion this function should return zero. If a non-zero value is returned from the plugin_init function, OpenAlias will assume that some error condition was encountered in initialization and will unload the plug-in.

We suggest doing the bare minimum in the plugin_init function. Allocate any user data that the plug-in will need but do not allocate any OpenAlias classes.

plugin_exit()

This function must be declared as:

extern "C" PLUGINAPI_DECL int plugin_exit( void ) 

It is called when the plug-in is removed or AliasStudio is shut down. The programmer uses this function to do whatever is necessary to clean up the plug-in. This function should remove the plug-in from all menus using the deleteObject() method for each declared AlFunctionHandle and destroy any created AlFunctions using the deleteObject() method. On successful completion this function should return zero. Failing to remove all references may result in core dumps if they are accessed by AliasStudio after the plug-in has been removed. In addition, ensure that any callbacks that have been installed are removed.

Static destructors and exiting from AliasStudio

Plug-ins are not unloaded when AliasStudio exits. This decreases the time that AliasStudio takes to exit but results in static destructors of C++ classes not being called. The DSO is not explicitly unloaded but is removed when AliasStudio exits.

Static destructors are called when a plug-in is unloaded manually. We suggest not doing operations within the static destructors of your classes. Note that plugin_exit() is still called when AliasStudio exits, so any saving of data can be done within that function.

Adding your plug-in to the UI

Momentary, Continuous and History plug-ins

There are three kinds of tools in AliasStudio: momentary functions, continuous functions and construction history functions.

Momentary tools

A momentary function is essentially a single event: a function that takes no arguments and has no return value. It is simply a jump point in memory. AliasStudio transfers control to this point when the menu item is invoked, the function executes, and control is returned to AliasStudio. An example of a momentary function is `delete all'.

The simplest way to define a momentary function is through the AlMomentaryFunction class. Calling one of the create methods on an instance of that class will generate a new momentary function in AliasStudio.

Continuous tools

A continuous function is more complicated than a momentary function. It is event driven: it must respond to user interaction through the mouse, keyboard, and user defined devices. As a result, continuous functions do not actually take over control of AliasStudio when they execute. Rather, they define a context under which UI events are interpreted. AliasStudio reads these events and transfers them to a group of event handlers defined by the continuous function. Thus a continuous function is a set of five callback functions: init, mouse down, mouse move, mouse up and cleanup.

In addition, a continuous function is allowed to define pre-init and post-cleanup functions. The reason for these is that there are two ways to enter and leave the context of a continuous function. The function can be preempted temporarily by selecting some momentary function, or terminated more permanently by switching contexts to some other continuous function (or quitting AliasStudio, of course). The pre-init and post-cleanup functions allow you to define actions that get invoked when AliasStudio truly changes contexts and does not merely preempt your continuous function.

> Init

"Init" is executed when the context of a continuous function is entered. This occurs when the function is first invoked, or after returning from a momentary function which pre-empted the function, or after reselecting this continuous function after completing some other continuous function.

> Mouse Down

"Down" is called on every mouse down event. Use this function to detect the start of a click-drag type of operation.

> Mouse Move

"Move" corresponds to the sequence of mouse movement events that occur between a mouse down and a mouse up. A move event is generated for each significant mouse movement while a button is being held down. Further, "move" is called in the case that the enter key is pressed on the keyboard. Therefore, it is in the move function that keyboard input should be detected.

> Mouse Up

"Up" is called on mouse up events. Use this function to do things like commit transforms or create geometry corresponding to some sequence of mouse drags.

> Cleanup

"Cleanup" is called when the context of this function is left to perform some other action. For example, if the user is in the context of your continuous plug-in, and invokes some other momentary function, the cleanup function is called, followed by the momentary function, followed by the "init" function when AliasStudio returns to the context of the original continuous function.

Command history plug-ins

Command history plug-ins are very complex, and it is advisable not to attempt creating one until you have a firm understanding of momentary and continuous plug-ins.

Command history plug-ins are not menu-based but rather are invoked when geometry changes. You install a command plug-in by selecting a menu item. This calls plugin_init() to install the command in AliasStudio. Once installed, the command plug-in can run without your reselecting the command plug-in menu item.

A command or construction history object has knowledge of how it is created. The constructors of the object are tracked as well as the targets (outputs) of the command. When a constructor is modified, the geometry of the targets are rebuilt. A command history plug-in would program the behavior of the modifications of the targets based on the changes of the constructors.

See the documentation on AlCommand, AlUserCommand, and AlNotifyDagNode, as well as the examples constrainCVExample.cpp and polyHistoryExample.cpp.

When you are modifying geometry, the doUpdates() method of the geometry should be called with FALSE, and never called with TRUE. This is necessary since the command is being called from within the message system and updates generate messages. If updates are done during a command, then unnecessary messages are generated and that could create a loop where the command is responding to a message it generated.

Attaching a plug-in to a menu or palette

The first step in attaching plug-in functionality to a menu is to create an AlFunction object. The AlFunction class encapsulates information about what actions to perform when your plug-in is selected. It represents the `back end' of a menu item. In particular, a plug-in would create an AlMomentaryFunction or an AlContinuousFunction to describe its operation.

To actually define the menu item which will invoke that AlFunction, use the AlFunctionHandle class, which provides a C++ interface to Scheme.

The file AlFunctionHandle.h must be included by the plug-in to use AlFunctionHandle. AlFunction.h must be included to use either AlMomentaryFunction or AlContinuousFunction.

In the plugin_init() function of the plug-in the various methods of the AlFunctionHandle are used to define the menu item (or menu items) that is added to OpenAlias, and the AlFunction classes are used to register the functions which make up the functionality of the plug-in. In the case of using Scheme instead of AlFunctionHandle, plugin_init() would at some point invoke a Scheme file that performs the necessary UI initialization.

General momentary plug-in code would look like the following:

Includes:

	#include <AlLiveData.h> 
	#include <AlFunction.h> 
	#include <AlFunctionHandle.h> 

Declaration of function and function handles:

	static AlFunctionHandle h; 
	static AlMomentaryFunction hFunc; 

Attaching to the Studio menus, during plugin_init():

	// All plug-ins must initialize the universe 
	AlUniverse::initialize( ); 
	// Create the function and associate the  
	// callback to be invoked. 
	hFunc.create( "plugin_func_name", plugin_callback ); 
	h.create( "PluginFunction", &hFunc );  
	h.setAttributeString( "PluginFunction" ); 
	// Studio will search for icons named 
	// plugin_func_name.M and plugin_func_name.S 
	// in this directory. 
	h.setIconPath( makeAltPath( dirName, NULL ) ); 
	// Which menu to install on. 
	h.installOnMenu( "al_goto", FALSE /* top */ ); 
	// Let the user know where the plug-in is 
	AlPrintf( kPrompt, "Plug-in function installed under the 
'Utilities' menu."); 
	// All is ok, let Studio know. 
	return 0; 

Removing the plug-in from the menus at plugin_exit() time:

	// Cleanup any private programmed defined data. 
	if ( pluginData != NULL ) 
		removeData( pluginData ); 
		 
	// Remove the plugin from the menu and free the 
FunctionHandle. 
	h.deleteObject(); 
	hFunc.deleteObject(); 
	// All is ok, let Studio know. 
	return 0; 

Continuous plug-ins follow a similar programming pattern.

Declaration of function handles:

	static AlFunctionHandle h; 
	static AlContinuousFunction hFunc; 

Attaching to the Studio menus, during plugin_init():

	AlUniverse::initialize(); 
	// Continuous callbacks are specified. 
	hFunc.create( "Continuous", init_func, down_func, 
move_func, up_func, cleanup_func, TRUE ); 
	hFunc.setPrompt( my_prompt, inbuf, kFilterNone ); 
	h.create( "Cont Test", &hFunc ); 
	h.setAttributeString( "cont" ); 
	h.setIconPath( makeAltPath( dirName, NULL ) ); 
	h.addToMenu( "mp_objtools" ); 
	AlPrintf( kPrompt, "Continuous example installed on Palette 
'Object Edit."); 
	return 0; 

Removing a plug-in's menus at plugin_exit() time is the same for both continuous and momentary plug-ins.

Menu and palette IDs for attaching a plug-in

Some of the menu items listed may not be available to you. For example, they may not have been purchased as part of your package or may not be available on your platform.

Menus

Menu Name
OpenAlias Menu Identifier
Animation
ap_animwinds 
Canvas
 
Construction
mp_grid 
Curves
al_curvetoolbox 
Curve Edit
mp_crvtools 
Delete
al_delete 
DisplayToggles
mp_display 
Edit
al_edit 
Evaluate
mp_evaltool 
File
al_file 
Help
al_help 
Layers
ma_layers 
Layouts
mp_window 
Locators
al_locate 
Mesh
 
ObjectDisplay
mp_objdisplay 
Object Edit
mp_objtools 
Paint
 
Paint Edit
 
Pick
mp_pick 
Point Clouds
al_cloudtoolbox 
Preferences
al_envtools 
Render
rp_render 
Surfaces
mp_buildsurf 
Surface Edit
mp_srftools 
Transform
mp_xform 
Utilities
al_goto 
View
mp_views 
Windows
mp_windows_menu 

Submenus

Menu Names
OpenAlias Menu Identifiers
Animation > Create
 
Animation > Edit
 
Animation > Editors
 
Animation > IK
 
Animation > Keyframe
 
Animation > Pick
 
Animation > Show
 
Animation > Tools
 
Blend Curve > Constraint continuity
mo_blend_crv_continuity 
Blend Curve > Constraint curvature type
mo_blend_crv_extension 
Blend Curve > Constraint direction type
mo_blend_crv_direction 
Blend Curve > Constraint interpolation direction
mo_blend_crv_toggle 
Blend Curve > Curve degree
mo_blend_crv_degree 
Blend Curve > Curve knot spacing
mo_blend_crv_parameter 
Curve > Arcs
mo_arc_funcs 
Curves > Lines
mo_line_funcs 
Curves > Lines (tangent)
mo_linetan_funcs 
Curves > New curves
mo_curve_funcs 
Curves > Primitives
 
Curve edit > Create
curve_create_sub 
Curve edit > Cut
mo_cut_funcs 
Curve edit > Modify
modify_types_sub 
Curve networks > Add/Delete sculpt
mo_cnet_add_control_sub 
Curve networks > Add/Subtract curves
mo_cnet_add_sub 
Curve networks > Continuity
mo_cnet_continuity_sub 
Curve networks > Influence weights
mo_cnet_weight_sub 
Curve networks > Region of influence
mo_cnet_region_sub 
Delete > Painting
 
Delete > Animation
 
DisplayToggles > Object toggles
 
Display Toggles > Window toggles
mp_display_window_sub 
Edit > Duplicate
 
Evaluate > Continuity
curvature_sub 
Evaluate > Surface evaluate
 
File > Export
al_export_types_sub 
File > File References
 
File > Import
al_import_sub 
File > Open Recent
 
Layers > Delete
layer_delete 
Layers > Select
layer_select 
Layers > Set state
layer_states 
Layers > Symmetry
layer_symmetric 
Layers > Visibility
layer_visibility 
Layouts > All windows
windows_all_sub 
Layouts > User windows
windows_load_save 
Locators > Deviation
al_locate_deviation_sub 
Locators > Measure
 
Mesh > Mesh Partitioning
 
Mesh > Mesh Curves
 
Mesh > Mesh Cleanup
 
Mesh > MeshPolyset
 
Object Edit > Align
 
Object edit > Attach
attach_types_sub 
Object Edit > Normal tools
 
Object Edit > Dynamic Shape Modeling
 
Paint > Airbrush
 
Paint > Effectbrush
 
Paint > Eraser
 
Paint > Marker
 
Paint > PaintSymmetry
 
Paint > Pencil
 
Paint > Select
 
Paint > Shape
 
Paint > Solidbrush
 
Paint Edit > Modify layer
 
Paint Edit > Color correction
 
Paint Edit > Layer effect
 
Paint Edit > Layer operation
 
Render > Ambient Occlusion
 
Render > Create lights
rp_light_sub 
Render > Create texture projections
mo_object_projprims 
Render > Editors
 
Render > Multi-lister
mlist_sub 
Pick > Object types
obj_types_sub 
Pick > Point types
point_types_sub 
Point Clouds > Surfaces
cloud_surf_sub 
Preferences > Interface
interface_sub 
Preferences > Menus
 
Preferences > User options
user_options_sub 
Preferences > Workflows
 
Surface edit > Create CurvesOnSurface
create_COS_types_sub 
Surface edit > Shells
solid_types_sub 
Surface edit > Stitch
stitch_types_sub 
Surface edit > Trim
trim_types_sub 
Surfaces > Boundary surfaces
boundary_types_sub 
Surfaces > Draft surfaces
 
Surfaces > Multi-Surface blend
 
Surfaces > Planar surfaces
 
Surfaces > Primitives
mo_object_primitives 
Surfaces > Rolled edge
 
Surfaces > Round surfaces
 
Surfaces > Swept surfaces
sweep_types_sub 
Utilities > Command stepper
command_stepper_sub 
Utilities > SBD
 
View > Local move camera
camera_local_movements_t
ypes_sub 
View > World move camera
camera_world_movements_t
ypes_sub 
View > View 1:1
 
Windows > Editors
editor_sub 
Windows > Information
information_sub 
Transform > Local
pivot_types_sub 
Transform > Modify
prop_mod_types_sub 

Building Options Boxes

There are two ways of building Studio Option boxes for plug-ins. The new and preferred method involves using the AlEditor C++ class to define and manage the Option box. Prior to the introduction of this method, option boxes were built with Scheme files.

Building Dynamic Editors using the AlEditor class

The new technique for building editors in OpenAlias plug-ins is considered dynamic since a plug-in developer has control over when the editor is displayed. The older scheme editor boxes were only capable of being displayed when the user selected the tool from the menu.

Creating and managing editors follows general API programming principles:

To create the editor:

	apieditor = new AlEditor; 
	if ( apieditor == NULL ) 
		return sFailure; 
	if ( apieditor->create( "Dynamic Editor Example" ) != 
sSuccess ) 
		return sFailure; 

Once the editor has been created, UI components can be added to it:

	ComponentDescriptor *descriptor = NULL; 
	descriptor = new ComponentDescriptor("Enter some text"); 
	if ( apieditor->addString( descriptor, "Hello world!", 
idStringCallback ) != sSuccess ) 
		return sFailure; 
	addToDesciptorList( descriptor ); 
	 
	descriptor = new ComponentDescriptor("Separator"); 
	if ( apieditor->addSeparator( descriptor ) != sSuccess ) 
		return sFailure; 
	addToDesciptorList( descriptor ); 
	descriptor = new ComponentDescriptor("Check Box"); 
	if ( apieditor->addCheckBox( descriptor, checkValue, 
idCallback ) != sSuccess ) 
		return sFailure; 
	addToDesciptorList( descriptor ); 

The AlEditor class assigns dynamic identifiers to the descriptor for the UI component. Each descriptor must be kept for later look up and matching so that the correct action can be performed.

We then open the editor:

	if ( apieditor->open() != sSuccess ) 
		return sFailure; 

To close the editor:

	if ( apieditor->close() != sSuccess ) 
		return sFailure; 
	if ( apieditor->deleteEditor() != sSuccess ) 
		return sFailure; 
	deleteDescriptorList(); 
	delete apieditor; 

Events associated with the UI component on the editor are passed to the plug-in via a callback that is defined by the programmer.

	if ( apieditor->addCheckBox( descriptor, checkValue, 
idCallback ) != sSuccess ) 
		return sFailure; 
	... 
static void idCallback( ComponentId id ) 
{ 
	ComponentDescriptor *descriptor = idToDescriptor( id ); 
	if ( descriptor == NULL ) 
		return; 
	if ( matchDescriptorToLabel( descriptor, "Check Box" ) ) 
	{ 
		HandleCheckBoxFunction(); 
	} 
} 

Scheme Editors

The alternate and original way of creating Studio editors utilizes a scheme interface. This technique is described next.

> First scheme file

If the plug-in requires an option box, the first scheme file is used to define it. All AliasStudio option boxes are written in Scheme, which is described below. For many examples of option box files, examine the .o.scm files that reside in subdirectories in the AliasStudio install area.

> Second scheme file

If the plug-in requires an option box and the option box requires default values for its variables, these default values are set in this file, which is invoked from plugin_init().

> Creating symbols and strings in Scheme

To pass information around in scheme,we define symbols as place-holders for values, and strings as names for those values. This is done through two primitives, ui-symbol and ui-string. These functions add elements to symbol tables inside of AliasStudio, unlike the define primitive which adds a symbol to Scheme's symbol table. In places where the name of a symbol is desired (e.g. the label for a menu item), a search is done of the string symbol table for a string with the same symbol name as the given symbol. If one is found, the associated string is used. If not, the actual symbol name is used instead. So, for example:

		(ui-symbol 'my_symbol 16) 

The default value of the symbol will be 16. No ui-string is associated with this symbol. Therefore, the text "my_symbol" will appear whenever the name of this symbol is displayed.

	Example 2:
	(ui-string 'my_symbol "Number of Cows")
	(ui-symbol 'my_symbol 16) 

In this case a ui-string is defined with the same symbol name. When the name of the symbol "my_symbol" is required, a search is done for a ui-string with the same name. Here one is found, and the text "Number of Cows" will be used as the name of this symbol.