Adding a new operator

Overview

To write an operator, the steps are loosely as follows:

  1. Use the xmledit program to create an XML file. This file will tell VisIt's code generation suite what the attributes are for your operator
  2. Run xml2plugin. This will do the code generation.
  3. Modify avt<operator-name>Filter.C
  4. Compile and run!

Location

If an operator is going to be located in the repository, it should go under /src/operators/<operator-name>.

xmledit

Invoking xmledit

You should start by running xmledit. This will allow you to specify the attributes for your operator. An example of attributes are three doubles to specify a normal for a plane for the slice operator.

If you are running in the repository, xmledit is located under /src/bin/xmledit. If you are running against an installed version of VisIt, use /path/to/visit/bin/xmledit.

xmledit takes one argument on the command line. That is the name of the .xml file it should generate. Our convention is to name the file <operator-name>.xml. You can start with an empty file, or you can copy an existing .xml file from another directory and rename it to be <operator-name>.xml. For the former approach, you need to figure out what the values are for each field. For the latter approach, you can simply modify the "right answers" from the existing file.

% cp ../Revolve/Revolve.xml ./<operator-name>.xml
% xmledit <operator-name>.xml

Using xmledit

You will need to modify various fields under the tabs xmledit presents.

Plugin tab

Name = <operator-name>
Label = <operator-name>
Version = 1.0
Plugin type = Operator
Turn off "Has icon" (or turn it on if you create a thumbnail for the plugin)

Makefile tab

No changes

Attribute tab

Name = <operator-name>Attributes
Purpose = This class contains attributes for the <operator-name> operator.

Enum tab

If you need enumerated types when setting up your attributes, do that here.

Fields tab

You will need to add a field for each input you want to your operator. VisIt has a bug where you must have at least one attribute. You create an attribute by clicking on "New" in the lower left.

You then modify:

Name = name to be used internally
Label = name used when presenting to the user
Type = the type of attributes
Initialization values

For example, if I was setting up the normal of the slice plane, I would do:

Name = planeNormal
Label = Normal of the slice plane?
Type = Double array (set length to 3)
Initialization Values = 0 0 0

Functions, Constants, Includes, Code

These are all for advanced use and you likely don't need them.

xml2plugin

xml2plugin will code generate much of the plugin for you.

Specifically:

  • A Qt-based user interface for the GUI
  • A Python-based user interface for the CLI
  • A Java-based user interface
  • The bindings VisIt needs to know that it is a plugin and how to load it
  • A shell of the module that fits into VisIt's data flow network scheme that does the "real work".
  • A makefile to build your plugin.
  • A class that contains the attributes for your plugin. The attributes map directly to what you set up in the fields tab of xmledit.
    • If you are using xml2plugin from repo, it will put your plugin in the same location as all of the other plugins
    • If you are using xml2plugin from an installed version of VisIt, it will put your plugin in your home directory. VisIt will locate this plugin at startup and use it.

If you do not like the Qt-based user interface generated by xml2plugin, you can, of course, code up your own. It is recommended that you do not modify the python- or java-based user interfaces or the "Attributes" or "Info" files.

Compiling

You should be able to say "make". The compilation will fail when it tries to compile avt<operator-name>Filter.C, since it is just a shell. However, the parts that you have to implement are set up to cause compilation failures (meaning that they are easy to identify).

Modifying the AVT file

Look here to see how to implement an avtFilter.

There are some special considerations for plugins, however:

  1. The filter must inherit from avtPluginFilter, although it can (and will) inherit multiply from another avtFilter.
    • avtPluginFilter defines the interface for plugins, namely how to "Create", "SetAtts", and determine if it is "Equivalent".
    • Some common multiple inheritances have already been set up, such as avtPluginDataTreeIterator, which inherits from avtPluginFilter and avtDataTreeIterator.
  2. You have direct access to the attributes as a data member of the class.
    • You cannot assume the attributes are valid in the constructor
    • You can assume the attributes are valid after "SetAtts" is called.

Examples

Simple example

The simplest possible example is just to return the input:

 return in_ds;

More complex example

If you want to scale a rectilinear data set in the X dimension by 3, here's code to do it:

vtkDataSet *
avtMyOperatorFilter::ExecuteData(vtkDataSet *in_ds)
{
     vtkRectilinearGrid *rgrid = (vtkRectilinearGrid *) in_ds;
     vtkDataArray *X = rgrid->GetXCoordinates();
     int nX = X->GetNumberOfComponents();
     for (int i = 0 ; i < nX ; i++)
         X->SetTuple1(i, X->GetTuple1(i)*3.0);
     return rgrid;
}

There's a lot wrong with the above:

  1. It assumes the input is rectilinear
  2. It modifies the input to the filter. That input could be used elsewhere, so this is bad.
  3. It doesn't do anything with memory management.

Running

That's it! After compiling your avtFilter, you should be able to run and see it in the user interface.

Here are some suggested topics that folks might want to add to this Wiki page

  • How operators work
  • Getting started
  • The state object
  • The filter
  • The operator GUI window