Example

The XOP1 Sample XOP

XOP1 is the simplest of all XOPs. It merely adds one to each element of an IGOR wave. XOP1 illustrates the structure of an XOP.

Usually communication between IGOR and the XOP is initiated in response to a user action. In the case of XOP1, it is called only when the user invokes the XOP1 operation from IGOR's command line or from a procedure (user-defined function or macro). For example, from the IGOR command line, the user could execute the following commands:

Make testWave=0        // Make a test data set.
Display testWave       // Display it in a graph
XOP1 testWave          // Add one to the test wave.

In creating an Igor Pro 5-compatible external operation, you start by creating a plain-text template that tells Igor the syntax of your operation. For XOP1 the template is "XOP1 wave". Igor takes this template and generates starter C code. You fill in the starter code to perform the desired operation and then compile it in your development system.

When your external operation is first invoked, you register it with Igor (by calling RegisterXOP1 in this case). Each time your external operation is invoked, Igor calls your "execute" operation (called ExecuteXOP1 in this case), passing to it a structure containing the parameters specified by the caller. Your execute operation then performs the requested operation.

// XOP1.c -- A sample Igor external operation
         
#include "XOPStandardHeaders.h"    // Include ANSI headers, Mac headers, IgorXOP.h, XOP.h and XOPSupport.h
#include "XOP1.h"
         
// Global Variables (none)
         
/*   DoWave(waveHandle)
         
     Adds 1 to the wave.
*/
static void
DoWave(waveHndl waveHandle)
{
     float *fDataPtr;             // Pointer to single-precision floating point wave data.
     double *dDataPtr;            // Pointer to double-precision floating point wave data.
     long npnts, i;
     
     npnts = WavePoints(waveHandle);    // Number of points in wave.
     switch (WaveType(waveHandle)) {
          case NT_FP32:
               fDataPtr = (float*)WaveData(waveHandle);   // DEREFERENCE - we must not cause heap to scramble.
               for (i = 0; i < npnts; i++) {
                    *fDataPtr += 1.0;
                    fDataPtr += 1;
               }
               break;
         
          case NT_FP64:
               dDataPtr = (double*)WaveData(waveHandle);  // DEREFERENCE - we must not cause heap to scramble.
               for (i = 0; i < npnts; i++) {
                    *dDataPtr += 1.0;
                    dDataPtr += 1;
               }
               break;
     }
}
         
/*   XOP1(wavH)
         
     Carries out operation described at top of file.
     Returns 0 if everything allright or error code otherwise.
*/
static int
XOP1(waveHndl wavH)   // Handle to data structure describing wave.
{
     switch (WaveType(wavH)) {
          case NT_FP32:           // Single precision floating point.
          case NT_FP64:           // Double precision floating point.
               break;
          case TEXT_WAVE_TYPE:    // Don't handle text waves.
               return NO_TEXT_OP;
          default:                // Don't handle complex or integer for now.
               return NT_FNOT_AVAIL;
     }
     
     DoWave(wavH);
     return 0;
}
         
#include "XOPStructureAlignmentTwoByte.h"  // All structures passed to Igor are two-byte aligned.
struct XOP1RuntimeParams {                 // We receive this structure from Igor when our operation is invoked.
     // Flag parameters (none).
     
     // Main params.
     
     // wave
     int waveEncountered;
     waveHndl waveH;
     int main1ParamsSet[1];
         
     // These are postamble fields that Igor sets.
     int calledFromFunction;               // 1 if called from a user function, 0 otherwise.
     int calledFromMacro;                  // 1 if called from a macro, 0 otherwise.
};
typedef struct XOP1RuntimeParams XOP1RuntimeParams;
typedef struct XOP1RuntimeParams* XOP1RuntimeParamsPtr;
#include "XOPStructureAlignmentReset.h"
         
/*   DoXOP1Operation(operationInfo)
         
     DoXOP1Operation is called when the user invokes the XOP1 operation from the
     command line, from a macro or from a user function.
     
     It returns 0 if everything went OK or error code otherwise.
*/
static int
ExecuteXOP1(XOP1RuntimeParamsPtr p)
{
     waveHndl waveH;                       // Handle to wave's data structure.
     int result;
     
     // Get parameters.
     if (p->main1ParamsSet[0] == 0)
          return NOWAV;                   // Wave parameter was not specified.
     waveH = p->waveH;
     if (waveH == NULL)
          return NOWAV;                   // User specified a non-existent wave.
         
     // Do the operation.
     result = XOP1(waveH);
     if (result == 0)
          WaveHandleModified(waveH);      // Tell Igor to update wave in graphs/tables.
         
     return result;
}
         
static int
RegisterXOP1(void)         // Called at startup to register this operation with Igor.
{
     char* cmdTemplate;
     char* runtimeNumVarList;
     char* runtimeStrVarList;
         
     // NOTE: If you change this template, you must change the XOP1RuntimeParams structure as well.
     cmdTemplate = "XOP1 wave";
     runtimeNumVarList = "";
     runtimeStrVarList = "";
     return RegisterOperation(cmdTemplate, runtimeNumVarList, runtimeStrVarList, sizeof(XOP1RuntimeParams), ExecuteXOP1, 0);
}
         
static int
RegisterOperations(void)   // Register any operations with Igor.
{
     int result;
     
     // Register XOP1 operation.
     if (result = RegisterXOP1())
          return result;
     
     // There are no more operations added by this XOP.
     
     return 0;
}
         
/*   XOPEntry()
         
     This is the entry point from the host application to the XOP for all
     messages after the INIT message.
*/
static void
XOPEntry(void)
{     
     long result = 0;
     
     switch (GetXOPMessage()) {
          // We don't need to handle any messages for this XOP.
     }
     SetXOPResult(result);
}
         
/*   main(ioRecHandle)
         
     This is the initial entry point at which the host application calls XOP.
     The message sent by the host must be INIT.
     
     main does any necessary initialization and then sets the XOPEntry field of the
     ioRecHandle to the address to be called for future messages.
*/
HOST_IMPORT void
main(IORecHandle ioRecHandle)
{
     int result;
     
     XOPInit(ioRecHandle);            // Do standard XOP initialization.
         
     SetXOPEntry(XOPEntry);           // Set entry point for future calls.
         
     if (result = RegisterOperations()) {
          SetXOPResult(result);
          return;
     }
     
     SetXOPResult(0);
}

Last updated: Tuesday, December 7, 2004