For all of you programmers out there that use the Alias OpenAPI, i hope you find some of this stuff useful. This page contains a whole bunch of tools, some source, a list of bugs, and a bit of advice.
You can also visit the PPT home page for some of my plug-ins.
If anyone out there is writing plug-ins that use messages or construction history, you might want to look at the results of my recent extensive experiments on messaging behaviour in Alias.
New! I've written complete reference guides for both pptUtil and pptGeometry. You'll find them in the help directory on this site (and in the tar file).
Check out my improved documentation search facility.
You can also browse through the directory
of tools or get a tar
file of the tools and source
(491520
bytes). The version-7.5 release of the PPT plugins is also
available in a tar
file
(931840
bytes).
Many of my tools are written in a scripting language called
Perl. I believe it comes with the
Irix distribution now, and should reside in /usr/sbin/perl
.
If you haven't got it, you can get a binary
distribution of Perl from this site.
These are a few quick utilities
i've written for searching through the OpenAPI header files.
class
will
search for classes by name, parse the appropriate header file,
and show you the list of prototypes in a given class. To know
which header file to read, it relies on a file named
.oadir
(in the same directory as the script), which can be generated
using hier
,
another script that scans all the header files in the OpenAPI
include/
directory. If you symlink
class
to proto
and run that, you can do a substring search for methods in all classes.
Finally, the func
script searches for keywords in a text file containing a hierarchy
of descriptions. It looks in a file called
.oacap
which i wrote to summarize the capabilities of various kinds
of API functions, so you can search for related words.
More detailed README files are available for
class
,
func
,
hier
, and
proto
.
This perl script is a handy utility
that parses your C++ program and inserts code to print debugging
messages to tell you about the arguments to function calls, their
returned values, and to let you know whenever memory is being
allocated or freed during runtime. I've found it very helpful
for debugging some of my plug-ins.
The README file
for cdebug
is available here.
Although the syntax for option-box Scheme files is pretty
hazy, this syntax checker (based on knowledge from observation
and experimentation) catches most common problems with Scheme
files, and i've found it pretty handy for making sure that my
option boxes are written right.
Some strange behaviours aren't immediately obvious, so
it's best to check with slint
right
at the start to be sure that your file is correct. (For instance,
if you don't declare your editor symbols properly, they won't
remember their values independently when there are multiple
instances of your plug-in on a shelf. Or if you don't explicitly
make your symbols floating-point by including the period, your
widgets might not appear.)
To use it, you'll need the main
perl script and a set of Scheme
primitive definitions to go with it. See the
README file for
slint
.
This script makes life a lot
easier for those of us who have to compile things. Did you ever
write a Makefile
and wonder why the heck you had to
repeat all this work when it's obvious to the computer, just by
looking at your C source, what header files it depends on? Well,
makeitso
does that for you and a bit more.
Under good conditions, you should be able to just write your source,
type makeitso
and have it
made, so.
To use it, just download the perl script and read the README file for details.
New! I've written complete reference guides for both pptUtil and pptGeometry. You'll find them in the help directory on this site (and in the tar file).
I've collected some useful routines for programming plugins. Hopefully they can save you some time and hassle; i use them in all of the PPT plugins. They take care of some gritty work and some of the routines are safer to use. In particular:
UIGetString
(and friends) in these plug-in utilities.
They will let you specify ranges or default values, too, so that
the results are predictable. Examples:
UIGetDouble("followcurve.start", start, 0.0, 1.0); UIGetString("reloctex.pat", pat, 1024);
UISetup
routine will do all the initialization
you need for your plug-in, including setting the icon path, scheme
file (if any), and menu or palette to attach to. You give the menu
or palette name as it appears in Alias, so you don't have to
remember the silly names. Here's how you can
set up your plug-in with one statement:
This will install your plug-in as a selection named "Plug-In" on the "Object Display" menu, label the icon with "icontag", and set up the Scheme files "plugin.scm" using the option-box symbol named "plugin.options".return UISetup(path, cfunc, handle, "Plug-In", "icontag", "ObjectDisplay", "plugin.scm", "plugin.options");
help/
subdirectory, a single
call will enable Ctrl-Alt-Shift-click to pop up a help window.
Another single call will cause your plug-in to display a welcome
message the first time it is run after installation, asking the
user if she'd like to see the help file.
aliasWarning
program. You can specify a title
string, any number of buttons, and any amount of text.
AlUniverse
that
will take care of the recursion for you.
The utilities are available here:
Also, if you're doing 3-D geometry calculations in your plug-in,
you might like pptGeometry.h
,
a set of classes for easy manipulation of points, lines,
and planes.
There are methods included for construction, intersection, and handy
creation directly from AlPolysets, AlPolygons, AlSurfaceCVs, and AlCurveCVs.
The above and more are all available in
the source directory
on this site.
AlPromptBox
can leave garbage in the answer variable
if you click "Yes" in a Yes-No-Cancel prompt box. Set the variable
to "Yes" first, and then as a precaution assume the answer was "Yes"
unless you get kNo
or kCancel
.
(Also, the box is limited to four lines of text. For a more reliable
and versatile prompt box, use promptbox()
in my
ppt utilities package.)
There's a bug in the rebuilding of option-box widgets. If you have the "rebuild" property set on an input widget, only the visible parts of the option box are rebuilt; parts within a collapsed group end up wrong or missing.
AlUniverse::setCurrentStage()
does not set
the working level, as it should. The check box gets highlighted
in the Stage Editor window but the working level isn't really
changed.
After calling AlPickList::popPickList()
,
pickstates may show up incorrectly. You can usually get around this
by always doing both ->pick()
and ->unpick()
on the object (to force the internals to recognize a change in state).
Doing a single pick is order n. The time it takes is
proportional to the size of the pick list. This means that picking 10
objects takes one hundred times as long as picking one object. If you
try to ->pick()
more than 1000 things, be prepared to
wait a few minutes.
Plug-ins will not get any keyboard input when there are no modeling windows open. The "Enter" key is ignored and does not produce a "down" event.
There is no access to the texture files in AlLight
parameters that are texture-mapped.
The ImagePlane::setImageFile()
method does not set
the image plane on a camera. In fact, it will change the string
variable you pass it to match the image plane name instead of the
other way around.
The AlSnap::toCV()
routine claims to snap to
"the closest curve, surface, polyst, or CurveOnSurface CV".
There is no such thing as a CurveOnSurface CV object in the
API, so this routine can never give you one.
Last time i checked, AlSnap::toCurve()
never
snapped to anything.
The routines AlCurve::isDisplayModeSet
and
AlSurface::isDisplayModeSet
return you the
opposite of the values they should for kDisplayGeomCVs
.
(They will say TRUE when the display mode is not set and FALSE when
it is.) Note that on the other hand, AlPolyset::isDisplayModeSet
will tell the truth.
Actually, both isDisplayModeSet
and setDisplayMode
seem to be fairly broken
for curves and surfaces. You can't read most of the display
modes properly, and you only seem to have control over CVs;
the rest of the modes all go on or off together with them.
There's a display problem with picking (i think that
pushPickList
and popPickList
still
aren't working right). If you pick a CV from your plugin and
then unpick the same CV, the entire surface will be lit and
all the CVs will vanish even though the pick list is empty.
The API pick routines do not respect curve or surface periodicity.
This means that if you try to pick a CV on the part of a closed curve
near the closure seam, there are really two coincident CVs there but
AlPickable::pick()
will only pick one. (The rest of
the package understands this and manipulates both together.) There
is no way from the API to pick both, because there is only one CV object.
The method AlPickable::asPickablePtr()
is missing.
Be sure to set the AW_DEBUGGING
environment
variable to YES
if you are debugging 7.5. Otherwise,
you'll get a security error.
Be careful when creating geometry! Note the inconsistency:
when you create a polyset, the AlPolysetNode is automatically
created for you; but when you create a curve or surface,
you must create the AlCurveNode or AlSurfaceNode yourself with the
AlCurve or AlSurface as a parameter to the create
method.
The documentation doesn't tell you which way CVs are ordered
when you get a matrix of CVs from an AlSurface
using CVsWorldPosition
and friends.
In the array you get, the V index changes faster than the U,
so the CVs will appear as
u1v1 u1v2 u1v3 ... u1vn u2v1 ... u2vn ... unvn.
If a polyset is looks red and turns orangish-yellow when you pick it, and is drawn strangely, with a single polyline through all the points, it could mean that the polyset contains some polygon with zero vertices.
After you setBlindData
, never free the data!
Alias will take ownership of that memory, despite the
misleading wording in the documentation on removeBlindData
.
If you're making derived classes from AlIterator
to
iterate over things, remember that the object wrapper passed to your
AlIterator::func
will be destroyed by the iterating
function. You should not delete
the object in your function.
Creating construction history on a polyset will prevent further operations on it of any sort -- even if you're not trying to change the polyset -- except for Smooth Vertices, which will always work and never do callbacks! See my messaging experiment results for more details.
Redrawing from AlUniverse::redrawScreen
is
sometimes faster with Toggle-Smooth on for unknown reasons.
If you were using the 7.0 API manual before, note the correction in menu names from "ap_time_tools" to "ap_timetools".
The behaviour of ST texture coordinates on polysets isn't very clear from the API docs at all. Here's what seems to be happening, as i've been able to determine after a bunch of experiments:
AlPolysetVertex::st
.
AlPolygon::st
.
AlPolysetVertex::setSt
method,
the S and T values are set for that vertex in the polyset and
for that vertex in each of the polygons that currently contain it.
AlPolygon::setSt
method, the
S and T values are set for that vertex in just that polygon, not
affecting any other texture coordinates.
When you are getting ST values and normals, check the
method prototypes! They sometimes accept float
s
and sometimes double
s, for no apparent reason.
Watch out, because passing double&
s to a routine
that accepts float&
s
may silently return you zeroes! The compiler signals this as
a "warning":
"pptPoke.c++", line 215: warning(3507): temporary used for initial value of reference to non-const (anachronism) polygon->st(0, ast.x, ast.y); ^ "pptPoke.c++", line 215: warning(3507): temporary used for initial value of reference to non-const (anachronism) polygon->st(0, ast.x, ast.y); ^but it does make a real difference. Yeah, i know it's stupid, but you have to make sure you pass exactly the declared type.
If you are a writing construction-history plug-in, don't use
multiple inheritance to define your AlUserCommand!! Even though
the example plug-in does this, it's not necessary. And when multiple
inheritance isn't absolutely necessary, it's best to stay away. Just
put the command-specific data you want to save into an ordinary
struct
and make that structure a member of your
AlUserCommand-derived class. I was stuck for a long time trying
to fix the save-and-retrieve routines in one of my plug-ins, and
couldn't figure out what was wrong. Finally i decided to do things
my own way instead of following the example, and upon switching to
a simple nested structure, everything worked.
Having any trouble setting joint limits? Remember to:
setUseTransforms
to activate the transforms you want on the joint.
setUseLimits
to activate the limits you want on the joint.
setTranslation
and friends won't have any effect.
Fix the rest pose with setRestPose
if necessary.
The meanings of open, closed, and periodic are mixed up all over the interface and documentation. Luckily the API distinguishes the three consistently. Here's an attempt to set things straight:
form
of kOpen
.
kClosed
by the API.
form()
method will return
kPeriodic
for such a curve. An example
is the default "circle" primitive, which has degree 3. It appears to
have 8 CVs, but there are really 11 (the last three are "stuck to" the
first three).
Then, according to these definitions, the
Object Edit->Close tool does not actually make curves
closed or open; rather it toggles them between [open or closed] and
[periodic]. All you have to do to make a curve
kClosed
is to move the last CV on top of
the first CV; but the only way to make it kPeriodic
is to use the Object Edit->Close tool.
The Information Window displays "Form OPEN" for both
open and closed curves, and displays "Form PERIODIC" for periodic curves.
Ever wonder how much memory you're really using? Here are the structure sizes of the API classes for 7.5.
class name | instance size in bytes |
---|---|
AlAction | 28 |
AlAimConstraint | 32 |
AlAmbientLight | 32 |
AlAnimatable | 4 |
AlArcAttributes | 32 |
AlAreaLight | 32 |
AlAttributes | 32 |
AlBoxLight | 32 |
AlCamera | 28 |
AlCameraNode | 56 |
AlChannel | 28 |
AlCharSnippet | 32 |
AlCharTransition | 28 |
AlCharacter | 36 |
AlCharacterSpace | 28 |
AlCluster | 28 |
AlClusterMember | 40 |
AlClusterNode | 52 |
AlClusterable | 4 |
AlCommand | 28 |
AlCommandRef | 28 |
AlConeLight | 32 |
AlConicAttributes | 32 |
AlConstraint | 32 |
AlContact | 28 |
AlContinuousFunction | 8 |
AlCurve | 36 |
AlCurveAttributes | 32 |
AlCurveCV | 56 |
AlCurveNode | 52 |
AlCurveOnSurface | 32 |
AlCylinderLight | 32 |
AlDagNode | 52 |
AlDebug | 1 |
AlDictionary | 36 |
AlDictionaryIterator | 4 |
AlDirectionLight | 32 |
AlEnvironment | 32 |
AlFace | 36 |
AlFaceNode | 52 |
AlFunction | 8 |
AlFunctionHandle | 4 |
AlGroupNode | 52 |
AlHashKey | 12 |
AlHashable | 28 |
AlIKHandle | 32 |
AlIKHandleNode | 52 |
AlImagePlane | 36 |
AlInput | 4 |
AlIntersect | 1 |
AlIntersectCurveCurveInfo | 56 |
AlIntersectCurveSurfInfo | 88 |
AlIntersectSurfSurfInfo | 24 |
AlIterator | 4 |
AlIteratorWithParent | 4 |
AlJoint | 28 |
AlKeyframe | 28 |
AlLight | 32 |
AlLightNode | 52 |
AlLineAttributes | 32 |
AlLinearLight | 32 |
AlLinkItem | 12 |
AlList | 12 |
AlMappedFieldItem | 16 |
AlMeasure | 1 |
AlMessage | 1 |
AlMessageTypeHandle | 4 |
AlMomentaryFunction | 8 |
AlMotionAction | 28 |
AlNameItem | 16 |
AlNonAmbientLight | 32 |
AlNotifyDagNode | 4 |
AlObject | 28 |
AlOrientationConstraint | 32 |
AlOrthographicCamera | 28 |
AlOutput | 4 |
AlParamAction | 28 |
AlParamItem | 20 |
AlPerformance | 1 |
AlPersistentID | 20 |
AlPerspectiveCamera | 40 |
AlPickList | 1 |
AlPickable | 4 |
AlPlaneAttributes | 32 |
AlPlayBack | 1 |
AlPlayFrame | 12 |
AlPointConstraint | 32 |
AlPointLight | 32 |
AlPolygon | 40 |
AlPolyset | 40 |
AlPolysetNode | 52 |
AlPolysetVertex | 56 |
AlRender | 1 |
AlResolutionItem | 32 |
AlRevSurfAttributes | 32 |
AlSet | 28 |
AlSetMember | 32 |
AlSettable | 4 |
AlShader | 32 |
AlShadingFieldItem | 16 |
AlShell | 28 |
AlShellNode | 52 |
AlSnap | 1 |
AlSphereLight | 32 |
AlSpotLight | 32 |
AlSurface | 36 |
AlSurfaceCV | 56 |
AlSurfaceNode | 52 |
AlTM | 128 |
AlTesselate | 1 |
AlTexture | 32 |
AlTextureNode | 56 |
AlTorusLight | 32 |
AlTrimBoundary | 32 |
AlTrimCurve | 36 |
AlTrimRegion | 32 |
AlUnits | 1 |
AlUniverse | 1 |
AlUserCommand | 8 |
AlUserPickItem | 24 |
AlUserPickList | 16 |
AlVertexDataList | 4 |
AlViewFrame | 1 |
AlVolumeLight | 32 |
AlWindow | 32 |
AlXevents | 1 |
If you are getting mysterious crashes deep inside the code, where your debugger shows you that some internal routine is crashing on a MA_free or something like that, a good way to debug this is to try removing (or commenting-out) lots of the "free" and "delete" calls in your own code. It may be that your program frees something too early: the deallocation causes no problems then, but later on an API internal routine expects to be able to free the memory too. Thanks to Jack Liao for suggesting this, which has since saved me many a time.
Mentioning this may make me sound a bit stupid, but anyway: be very careful not to re-free or re-delete things from a base class in the destructor for a derived class. When an object of a derived class is deleted, all destructors for the base classes will get automatically called after the object's own (derived class) destructor. I've been bitten by this a number of times. Maybe it's a result of programming too much in Python, where everything is simple and obvious and explicit (did i mention i like this Python language?).
Tip: when trying to grab screen shots (for example, using the "Grab" function of xv), if you get screwed-up colours, try switching to toggle-shade mode before grabbing.
The animation frame range types are all screwed up. There's an
AnimationRange
type in AlRender and an AlFrameRangeType
in AlUniverse. Both seem to do the same thing, but have different names and
values, so watch out! AlRender::AnimationRange will return the ones on the left;
then use the corresponding constant on the right as an argument to
AlUniverse::frameRange to get the right frame range.
AlRender::AnimationRange (AlRender.h) | AlFrameRangeType (AlUniverse.h) |
---|---|
AlRender::kGlobalRange (0) | kGlobal (2) |
AlRender::kMinMax (1) | kMinMax (1) |
AlRender::kTimeSlider (2) | kFromPreviewWindow (0) |
Look out! The silly MIPSpro C++ compiler will not warn you if you initialize a variable with data that completely does not match its structure! You'll just get garbage (or, at best, rearranged data) in your structure, with no warning at all.
If you get a crash immediately after loading a plug-in (and getting
the "Installed successfully" message), it may have something to do with
your options/user_options
file. In particular, if the
debugger shows you that the crash happens within the ELK Scheme
interpreter, try removing or replacing your options/user_options
file. This eliminated a strange crash i was experiencing (for apparently
no reason, using exactly the same plug-in binary that had worked the night
before).
Copying cameras from the API is broken. Don't try it. Attempting to
call ->copyOptions
on a camera AlGroupNode will produce this:
and, of course, a crash...