Go to AVS Home Page
 Recent Releases
OpenViz 2.8 Released
AVS/Express 7.2 Released
Toolmaster 7.5.3 Released
AVS 5.7 Released
 Contact Support
 Licensing
Information on licensing AVS 
products
 
Interact with the AVS Developer 
community. Takes you out of 
the support web site

 


AVS Technote

The Points Array and the Field Extent Information


The AVS field structure has two components whose usage in AVS seems to have caused some confusion: the points array and the min_extent/max_extent vectors. The point of this Technote is to explain our thinking in creating these structures, how we use them internally in the AVS code, and how people using AVS and developing applications can take advantage of them.

These components have different purposes and should be treated differently by modules. In the default cases, the extents information contains the minimum and maximum values of each component of the points array. There are cases which will be discussed later where these arrays are different. For the purpose of illustration, let's assume that in all the following examples, we are dealing with a 3D, 3-space AVS field.

The points array contains the information necessary to map an IJK value from its logical array position to its real position in physical space. In the case of irregular data, each data element has an explicit XY(Z) mapping associated with it. In the rectilinear case, there is a vector for each I, J and K which are indexed into by the index of the data value. In the uniform case, the points array contains the maximum and minimum coordinate for each of the dimensions in the field. This is represented by 2*nspace floating point values (where nspace is already >= ndim).

Here is the equation to compute the X coordinate (X) from a uniform field given the index of the sample (I):

    Min X coord = POINTS[0];
    Max X coord = POINTS[1];
    X = I * (Max X coord - Min X coord) / 
           (X dimension - 1) + Min X coord

The default points array for uniform data is [[0 to (MAXX(field)-1)], [0 to (MAXY(field)-1)], [0 to MAXZ(field)-1)]].

Rectilinear and irregular data pose little problem since the coordinate mappings are explicit, so we'll concentrate the rest of this discussion on uniform data.

The extents information contains the extent of the data in physical space. It is used primarily to set the window in the Geometry Viewer so that things appear centered and normalized. In many cases, the extents information is exactly the same as the points information. The only time it is different is when the user has explicitly set it to be so by using the AVS field file header, the user has written a module which modifies these values, or a supported AVS module (like crop) modifies the data.

There are times when you want the extent information to be different from the points information. The crop module is a good example. After cropping, you want the data to live in the correct place in physical space; the points information takes care of this. But you also want the Geometry Viewer to know the original spatial context of the data. The extents data stores this information. This mechanism makes the following network possible:

             READ VOLUME
         ________|__________
         |                 |
       CROP              CROP
         |                 |
   VOLUME BOUNDS     VOLUME BOUNDS
         |_________________|
                 |
          GEOMETRY VIEWER

The window in the Geometry Viewer will be normalized to the extent of the original volume, not the extents of either of the new (cropped) fields.

Another example might be when you have time-based data moving through space. To represent this properly, you might want to have the extents for each time-slice be set to the min and max extent of the entire time period and the points set to the extents for each individual time slice. In this way, the Geometry Viewer would retain the overall extent and not keep normalizing itself to the current time slice.

This is why the downsize and interpolate modules do not modify either the points or the extents information. What they do is adjust the resolution of the data sitting in a particular place in space. They don't readjust the space the data sits in.

Several individuals have called in with the following problem: they have data which is high resolution in X and Y and low resolution in Z (CAT scans, typically) and wanted to use the interpolate module to stretch the Z data so that it was in the correct space relative to X and Y. What the interpolate module did though, was only increase the resolution of the data in the Z direction without affecting the space it lived in. This gave unacceptable results for two reasons: it didn't solve the problem of the flat data, and it made even more data that was "wrong".

I've encouraged individuals with this type of problem not to use interpolate (because it "creates" new data which may not be correct for a given application), but to use the Transformation Selection in the Geometry Viewer instead to scale up the data in the Z direction without affecting the resolution. This same affect can be achieved by modifying the points array, but this is more difficult since it requires either writing a module or modifying the points data using the field file header. An example of this is provided at the end of this Technote.

Points information is always present in a field structure. Extents information may not always be set. The way to tell is to test the flags variable in the field structure against AVS_VALID_EXTENT.

        if (field->flags & AVS_VALID_EXTENT) {
            you've got an extent already set; 
        }

Here are the various library routines which manipulate the extents data and what they do:


AVSfield_set_extent(field, min_ext, max_ext)
AVSfield *field;
float *min_ext, *max_ext;

Overwrites the field's extent information with the contents of the min_ext and max_ext arrays.


AVSfield_get_extent(field, min_ext, max_ext) 
AVSfield  *field;
float *min_ext, *max_ext;

Overwrites the min_ext, max_ext arrays with the extents found in the field structure. If the extents are not present, it computes them and modifies both the field structure and the min_ext and max_ext arrays.


UTILget_world_coords(x, y, z, i, j, k, field) 
float *x, *y, *z;
int i, j, k; 
AVSfield *field; 

Fills in x, y, and z with the physical position of the field's i, j, and k node. This is valid for AVS2 and AVS3.0 but has a bug in it for data which is NOT 3-space.


UTILget_coord(field, i, j, k, x, y, z) 
AVSfield *field; 
int i, j, k; 
float *x, *y, *z;

Fills in x, y, and z with the physical position of the field's i, j, and k node. This only works for AVS4 or greater and works for non 3-space data as well.


UTILtransform_coords(new_x, new_y, new_z, old_x, old_y, old_z, field) 
AVSfield  *field;  
float *new_x, *new_y, *new_z;  
float  old_x,  old_y,  old_z;

Fills in new_x, new_y, and new_z with the transformed position of old_x, old_y, and old_z. This is like UTILget_coord except that it works on floating point positions rather than integer indices.


APPENDIX I

An example showing how to "stretch" data in the Z-direction by a factor of 2.0. This is done by creating a second file with the new points information in it and by setting the extents information in the field file header. When read in, this will display a 64*64*64 field which lives in the space [0..63, 0..63, 0..127]

NEW_HYDROGEN.FLD: (modified from /usr/avs/data/field/hydrogen.fld)
# AVS field file 
# this is a header file for a field to be 
# used in conjunction with the  build a field module of AVS
#
ndim = 3
dim1 = 64
dim2 = 64
dim3 = 64
nspace = 3
veclen = 1
data = byte
field = uniform
min_ext = 0 0 0
max_ext = 63 63 127

coord 1 file=NEW_ONE filetype=ASCII 
coord 2 file=NEW_ONE filetype=ASCII offset=2
coord 3 file=NEW_ONE filetype=ASCII offset=4
variable 1 file=/usr/avs/data/volume/hydrogen.dat filetype=binary skip=3

NEW_ONE: (file containing points information)
0 63
0 63
0 127

APPENDIX II

Here is the source code of a module which lets you set the contents of the points and extents arrays for 3D, 3-space uniform fields.

#include <stdio.h>
#include <avs/avs.h>
#include <avs/port.h>
#include <avs/field.h>
 
/* *****************************************/
/*  Module Specification                   */
/* *****************************************/
int points_and_extents_spec()
{
    int in_port, out_port, param;
    extern int points_and_extents_compute();

    AVSset_module_name("points and extents", MODULE_FILTER);

    /* Input Port Specifications               */
    in_port = AVScreate_input_port("input", "field 3D 3-space uniform", 
        REQUIRED);

    /* Output Port Specifications              */
    out_port = AVScreate_output_port("output", "field 3D 3-space uniform");
    AVSinitialize_output(in_port, out_port);

    /* Parameter Specifications                */
    param = AVSadd_float_parameter("points x min", 0.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("points y min", 0.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("points z min", 0.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("points x max", 1.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("points y max", 1.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("points z max", 1.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");

    param = AVSadd_float_parameter("extent x min", 0.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("extent y min", 0.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("extent z min", 0.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("extent x max", 1.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("extent y max", 1.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");
    param = AVSadd_float_parameter("extent z max", 1.0, 
       FLOAT_UNBOUND, FLOAT_UNBOUND);
    AVSconnect_widget(param, "typein_real");

    AVSset_compute_proc(points_and_extents_compute);
    return(1);
}
 
int points_and_extents_compute( input, output, pts_x_min, 
  pts_y_min, pts_z_min, pts_x_max, pts_y_max, pts_z_max, 
  ext_x_min, ext_y_min, ext_z_min, ext_x_max, ext_y_max, ext_z_max)
AVSfield *input;
AVSfield **output;
float *pts_x_min, *pts_y_min, *pts_z_min, *pts_x_max, *pts_y_max, *pts_z_max;
float *ext_x_min, *ext_y_min, *ext_z_min, *ext_x_max, *ext_y_max, *ext_z_max;
{
    int data_len;

    /* copy the data */
    data_len = AVSfield_prod((*output)->ndim, (*output)->dimensions) *
      (*output)->veclen * AVStypesize((*output)->type);
    memcpy ((*output)->field_data, input->field_data, data_len);

    /* set the points information */
    (*output)->points[0] = *pts_x_min;
    (*output)->points[1] = *pts_x_max;
    (*output)->points[2] = *pts_y_min;
    (*output)->points[3] = *pts_y_max;
    (*output)->points[4] = *pts_z_min;
    (*output)->points[5] = *pts_z_max;

    /* set the extent information */
    (*output)->min_extent[0] = *ext_x_min;
    (*output)->min_extent[1] = *ext_y_min;
    (*output)->min_extent[2] = *ext_z_min;
    (*output)->max_extent[0] = *ext_x_max;
    (*output)->max_extent[1] = *ext_y_max;
    (*output)->max_extent[2] = *ext_z_max;

    return(1);
}
 
AVSinit_modules() {
    AVSmodule_from_desc(points_and_extents_spec);
}