<<<<<<<<<<<<<<<<<

generic_function(function) FUNCTION generic_function(function)

NAME

generic_function - abstract class for function objects which can compute arbitrary functions on behalf of a dd_function_filter or ds_function_filter instance.

DESCRIPTION

The generic_function object is an abstract class that can be subclassed for the purpose of implementing arbitrary functions of points, scalars, normals, vectors, and texture_coords. It is intended that subclasses of generic_function be used as slave objects by the dd_function_filter and ds_function_filter. However, sophisticated users can, with care, directly specify input parameters, invoke the function, and retrieve computed values at script level. See the "NOTES" section for details.

SUPERCLASS

object

INSTANCE VARIABLES

MESSAGES

points= Specify the points from which a function is computed. Can be either a pointer (of type ARG_IS_FPTR) or a list of float values.

points+ Append the list of points from which a function is computed. Can be either a pointer (of type ARG_IS_FPTR) or a list of float values.

points? Return a pointer (of type ARG_IS_FPTR) to the list of points.

point_values? Returns the point values (floats) on the stack.

num_points? Returns the number of points.

call! Invoke the function defined in the subclass.

There are similar messages to each of the above for scalars, vectors, normals, and texture_coords.

NOTES

An understanding of how storage is allocated for each of the components (points, scalars, etc) is necessary only when one wants to directly supply values and retrieve computed results. Before sending a subclass instance a "call!" message, two things must be provided. The first is obvious: The script writer must provide the values from which the function is computed. For example, if the function computes scalars from vectors, the vectors must be supplied. The second thing that must be supplied is less obvious: The script writer must provide STORAGE for the results of the computation. In the previous example, storage for the scalars must be provided, and it must be large enough to accomodate one scalar for each vector in the previously supplied list of vectors. This can be done in the same way as for the vectors, except that the values used won't be relevant. After this is done, a "call!" message may be sent and the scalars may be retrieved.

It is also important to know that function objects "forget" the values and storage supplied once a "call!" message has been sent. Although they are available for retrival, they are not available for use by a subsequent "call!" message. The storage and/or values must be supplied again before each "call!". The reason for this is to protect against accidental modification of unused components. Consider as an example a function which computes scalars and normals from points. Consider what would happen if this function were used by both a dd_function_filter (which operates on display_data, and provides normals) and a ds_function_filter (which operates on datasets, and does not provide normals). If the function object were first used by the dd_function_filter, the normals would still be present when invoked by the ds_function_filter, and the storage for them would be erroneously modified.

EXAMPLE

The best example of how to use this class is to show how to subclass it. A fair amount of trickery goes on in this class to simplify writing of subclasses and reduce the number of internal messages that need to be overridden. The result is a small number of rules to remember in order to quickly creates functions (and, indirectly, custom filters).

Suppose you wanted to write a function that computed scalar values from the magnitude of vector data. This object currently exists in the function class library and is called vector_mag. Examine the .scr file for vector_mag:

class_generator new: vector_mag

file_name= vector_mag append_file= vector_mag.cls object_name= vector_mag super_object= generic_function append_instance_initialize=`append_init' ;

s_vector new: `append_init' = ( `MODIFY(SCALARS);' ) ;

vector_mag generate!;

It begins in the normal manner, defining the name of the object and the superclass. The significant part is the append_init section, where we identify the modifiable elements (the elements which are computed) via the MODIFY macro. There should be one invocation of this macro for each computed element. The argument should be the name of the component in capital letters. Optionally, this section can invoke the INITIALIZE macro (with identical usage) on any subset of the modifiable elements which should be pre- initialized with original values (like from a display_data object). The INITIALIZE macro should not be used on non- modifiable elements, because those elements are automatically initialized with their initial values. For example, if a function object wanted to compute scalars and normals from points and existing normals, the append_init section would be defined as follows:

s_vector new: `append_init' = ( `MODIFY(SCALARS);', `MODIFY(NORMALS);', `INITIALIZE(NORMALS);', ) ;

Once the .scr file is done, the .cls file can be written. The .cls file need only contain one function, and it must be named "function". It should be declared as follows:

static void function(instance) CLASS_NAME *instance;

For example, vetor_mag.cls contains the following:

#define VEC_DOT(x,y) ((x)[0]*(y)[0] + (x)[1]*(y)[1] + (x)[2]*(y)[2])

#define VEC_MAG(x) sqrt(VEC_DOT(x,x))

static void function(instance) VECTOR_MAG *instance; { int i; if (instance->vectors && instance->scalars) for (i=0;i<instance->num_vectors;i++) instance->scalars[i] = VEC_MAG(instance->vectors + 3*i); }

Note the computation loop. We check for the existance of the fields in question. Some fields may or may not be present. For example, normals would be set by dd_function_filter but not by ds_function_filter. Also, function loops over the number of vectors in this case. It is not necessary to check the size of the scalars storage, because the function filters ensure that the size of any modifiable component will be at least as large as any of the other components.

Although vector_mag defines no new instance variables, subclasses of generic_function may define any additional instance variables it may need to compute its function. For example, dipole_potential does this.

SEE ALSO

dd_function_filter ds_function_filter vector_mag dipole_potential


Please send comments and suggestions to
consult@rpi.edu