tabsub — macro expand an input table into an animation script


tabsub template_file < >> script


tabsub takes as input a data table on standard input (such as might have been produced by tabinterp(1) or similar tool), and a template file named on the command line. For each row (line) of the input table, one complete copy of the template file is output on standard output. As the template is output, any macro invocations in the template file are replaced with the data values from the input table's current row. In the input table, any blank lines or lines with a pound sign ('#') as the first character are ignored, allowing comments to be added to the input table.

Macro invocations in the template file all begin with an at-sign ('@'). In order to send an at-sign through to the output, a second at-sign must immediately follow it, e.g. when '@@' is encountered in the template, a single '@' is output. To output the data value found in a given channel in the current input row of the data table, the at-sign is followed by the channel number, e.g. to output the value in channel four, specify '@4', and to output the value in channel 42, specify '@42'. In some circumstances it my be desirable to highlight the difference between channel value substitution, and literal numeric values. To facilitate this, the channel number may be enclosed in parenthesis to explicitly delimit the macro invocation. For example, channel four could also be specified as '@(4)', and channel 42 as '@(42)'. This second notation is generally preferred.

The tabsub program is intended primarily for creating scripts relating to animation. To facilitate this, a variety of more complex macros also exist.


will output the row (line) number of the input table which is currently being processed, with the first line being numbered zero. This is useful for creating frame numbers, or other sequence tags in the output.


will output the time value which is always found in the left-most column of the current row.

The more complex macros can also take arguments. If the first character of an argument is an at-sign ('@') (or percent-sign ('%'), for backwards compatibility), then the number that follows signifies an input channel substitution as before. Otherwise the value is taken literally.

The rot macro is used to convert three Euler angles given in degrees into a rotation expressed as a 4x4 homogeneous transformation matrix.

@(rot x_angle y_angle z_angle)

The arguments may be either numeric constants, column value macros, or a combination of both. The matrix is generated by calling the libbn(3) routine bn_mat_angles which performs the rotation around the Z axis first, then Y, then X. For example, the macro

@(rot 0 0 45)

creates the following matrix, a 45 degree rotation about Z:

7.071067812e-01 -7.071067812e-01 0.000000000e+00 0.000000000e+00
7.071067812e-01 7.071067812e-01 -0.000000000e+00 0.000000000e+00
0.000000000e+00 0.000000000e+00 1.000000000e+00 0.000000000e+00
0.000000000e+00 0.000000000e+00 0.000000000e+00 1.000000000e+00

Similarly, the macro

@(rot @4 @5 90)

creates a rotation matrix where the angle of rotation around X is taken from input channel four, the Y angle is taken from input channel five, and the Z angle is fixed at 90 degrees.

The xlate macro converts three distances (which must be specified in millimeters if the output script is destined for processing by rt(1) or mged(1)) into a translation expressed as a 4x4 homogeneous transformation matrix.

@(xlate dx dy dz)

The matrix is generated by invoking the C macro MAT_DELTAS found in h/vmath.h. For example, the macro

@(xlate 100 -20 300) creates the following matrix:

1.000000000e+00 0.000000000e+00 0.000000000e+00 1.000000000e+02
0.000000000e+00 1.000000000e+00 0.000000000e+00 -2.000000000e+01
0.000000000e+00 0.000000000e+00 1.000000000e+00 3.000000000e+02
0.000000000e+00 0.000000000e+00 0.000000000e+00 1.000000000e+00

Similarly, the macro

@(xlate 13 @7 0)

creates a matrix where the origin is translated 13 units (mm) in X, and the number of units found in input channel 7 in Y. No translation occurs in Z.

The orient macro combines the operation of the rot zand xlate macros, and also offers optional scaling. The invocation is one of:

@(orient tx ty tz rx ry rz) @(orient tx ty tz rx ry rz scale)

where all rotation is done first, then the translation, and then the scaling (if given).

The ae command converts mged(1) style azimuth and elevation angle given in degrees into a rotation expressed as a 4x4 homogeneous transformation matrix.

@(ae azimuth elevation)

The matrix is generated by calling the libbn(3) routine bn_mat_ae

The arb_rot_pt command generates an arbitrary rotation matrix, expressed as a center of rotation, a second point defining the axis of rotation, and an angle of rotation expressed as an angle in degrees into a rotation expressed as a 4x4 homogeneous transformation matrix.

@(arb_rot_pt p1[x] p1[y] p1[z] p2[x] p2[x] p2[z] ang)

The matrix is generated by calling the libbn(3) routine bn_mat_arb_rot

The arb_rot_dir command generates an arbitrary rotation matrix, expressed as a center of rotation, a direction vector defining the axis of rotation, and an angle of rotation expressed as an angle in degrees into a rotation expressed as a 4x4 homogeneous transformation matrix. The direction vector need not be unit length.

@(arb_rot_pt pt[x] pt[y] pt[z] dir[x] dir[x] dir[z] ang)

The matrix is generated by calling the libbn(3) routine bn_mat_arb_rot

The quat command converts a quaternion into a 4x4 homogeneous transformation matrix.

@(quat x y z w)

The fromto command is used to rotate the given axis to point in the same direction as the vector formed by subtracting the 'next' point from the 'cur' point.

@(fromto axis cur_x cur_y cur_z next_x next_y next_z)

The axis argument must be one of these six strings: +X, -X, +Y, -Y, +Z, -Z, where the axis letter is capitalized. The matrix is generated by calling the libbn(3) routine bn_mat_fromto where the 'from' argument is derived from the axis given, and the 'to' argument is the unit-length difference 'next'-'cur'.


It is worth noting that the "anim" command in the rt(1) animation scripting language also offers some of these capabilities via built-in shortcuts. In addition to being able to provide the full 4x4 matrix as an argument to the "anim ... matrix" command, there are special keywords: "xlate", "translate" which take 3 arguments and pass them to the macro MAT_DELTAS, "rot" which takes 3 arguments and passes them to bn_mat_angles(), "scale" which takes one argument as a uniform scale factor (e.g. a value of 2 makes the subtree twice as large, scaled around the origin) and "scale_about" which passes 4 arguments (point and scale factor) and calls bn_mat_scale_about_pt(). An example of making an ellipse 1/5th the original size looks like this:

anim ellipse.r/ellipse.s matrix rarc scale_about 16.1309 46.6556 -3.72252 0.2;


Based upon the example started in the manual page for tabinterp(1), here is a Bourne shell script which will generate the necessary template file using a "here document", and then process the 8-channel output table left in the file "".

# This template will be instantiated once for each frame to be made.
cat << EOF > template

start @(line);
lookat_pt @(3) @(4) @(5);
viewsize @(7);
anim all.g/actor.g matrix rmul
 @(xlate @0 @1 @2);
anim all.g/light.r material rparam
 inten=@(6) angle=70 invisible=1;
! actor.pix.@(line);

# This is the start of the animation script, which will be appended to below.
cat << EOF > script
viewsize 3000;
eye_pt -4.429280979044739e+03 -1.633722950749571e+03 -1.624787858562220e+03;
orientation 5.435778713738288e-01 4.980973490458696e-01 4.564221286261679e-01 4.980973490458693e-01;
#frame data follows
# Append the data for each frame
tabsub ./template < >> script

The frame number is taken from the input table line number, and substituted into the start command. The main actor position is taken from channels 0,1,2 and applied (as an "articulation") to the matrix located along the arc between "all.g" and "actor.g" in the mged database. The camera (eye) position stays fixed for this animation, but the camera orientation is changed by substituting channels 3,4,5 into the lookat_pt command, and the viewsize (zoom lens setting) is changed by substituting channel 7 into the viewsize command. The argument to the light region's material property string is replaced with a new string that spells out the current light parameters. After the end command, a rt(1) shell escape is constructed, which will run a script called "" with the given argument (which has been arranged to be the file name of the pix(5) file that rt(1) just wrote, so that it can be post-processed, compressed, sent to a video recorder, etc.

Try clipping this example out of the manual page (usually found in /usr/brlcad/man/man1/tabsub.1) and running it.


In the tabinterp(1) manual page, mention was made of animating the flight of a rocket. This partial example outlines how that might be accomplished.

tabinterp << EOF >
# Channel allocations:
#   0,1,2 position of base of rocket
#   3,4,5 next position of base of rocket
# Input table column allocations:  time, X, Y, Z
file rocket.table 0 1 2;
times 0 4 60;
# Assign interpolators to output channels
interp spline 0 1 2;
# Get +1 "look ahead" on values, for auto-guidance
next 3 0 1;
next 4 1 1;
next 5 2 1;
cat << EOF > rocket.template

start @(line);
anim all.g/rot.g matrix rmul
 @(xlate @0 @1 @2);
anim rot.g/rocket.g matrix rmul
  @(fromto +Z @0 @1 @2 @3 @4 @5);
tabsub ./rocket.template < >> script

The items worthy of note are the use of the tabinterp(1) next command to place the position look-ahead into channels 3,4,5 and the matching use of the tabsub fromto macro to convert the current and next positions into an appropriate rotation. In this case, the central axis of the rocket as found in the mged(1) database rises up the +Z axis. Translating the rocket into position is handled one matrix higher up the tree, using the xlate macro.


rt style animation scripts can be processed by rt(1) and remrt(1) by giving the -M option on the command line, and providing the script on standard input. For example, the rocket animation might be run like this:

rt -M -V4:3 -w1440 -n972 -p90 -o rocket.pix rocket.g all.g < script

to produce images in NTSC ("Academy" 4:3) aspect ratio at double the normal resolution, suitable for later processing by pixhalve(1).

The same animation can be previewed in near real-time using mged(1). For this example, mged(1) would be started with

mged rocket.g

followed by attaching to an appropriate display device. Then, these commands would be given:

e all.g preview script

mged(1) will process each frame as fast as it can, and update the screen.


tabinterp(1), xyz-plot3(1), cut(1), paste(1), rt(1), mged(1)


There is presently a compiled-in limit of 1023 channels in the input table.




This software is Copyright (c) 1992-2016 by the United States Government as represented by U.S. Army Research Laboratory.


Reports of bugs or problems should be submitted via electronic mail to <>.