Yorick's graphics functions produce most of the generic kinds of pictures you see in scientific publications. However, providing the perfect graphics interface for every user is not a realistic design goal. Instead, my aim has been to provide the simplest possible graphics model and the most basic plotting functions. If you want more, I expect you to build your own "perfect" interface from the parts I supply.
My dream is to eventually supply several interfaces as interpreted code in the Yorick distribution. Currently, the best example of this strategy is the `pl3d.i' interface, which I describe at the end of this chapter. Not every new graphics interface needs to be a major production like `pl3d.i', however. Modest little functions are arguably more useful; the plh function discussed below is an example.
As you will see, the simplest possible graphics model is still very complicated. Unfortunately, I don't see any easy remedies, but I can promise that careful study pays off. I recommend the books "The Visual Display of Quantitative Information" and "Envisioning Information" by Edward Tufte for learning the fundamentals of scientific graphics.
Yorick features nine primitive plotting commands: plg plots polylines or polymarkers, pldj plots disjoint line segments, plm, plc, and plf plot mesh lines, contours, and filled (colored) meshes, respectively, for quadrilateral meshes, pli plots images, plfp plots filled polygons, plv plots vector darts, and plt plots text strings. You can write additional plotting functions by combining these primitives.
Yorick's plg command plots a one dimensional array of y values as a function of a corresponding array of x values. To be more precise,
plg, y, x
plots a sequence of line segments from (x(1),y(1)) to (x(2),y(2)) to (x(3),y(3)), and so on until (x(N),y(N)), when x and y are N element arrays.
The "backwards" order of the arguments to plg (y,x instead of x,y) allows for a default value of x. Namely,
plg, y
plots y against 1, 2, 3, ..., N, or indgen(numberof(y)) in Yorick parlance. You often want a plot of an array y with the horizontal axis (y is plotted vertically) merely indicating the sequence of values in the array.
Optional keyword arguments adjust line type (solid, dashed, etc.), line color, markers placed along the line, whether to connect the last point to the first to make a closed polygon, whether to draw direction arrows, and other variations on the basic connect-the-dots theme.
Specifying line type 0 or "none" by means of the type= keyword causes plg to plot markers at the points themselves, rather than a polyline connecting the points. Here is how you make a "scatter plot":
plg, type=0, y, x
For a polymarker plot, x and y may be scalars or unit length arrays. If you need to specify your own marker shapes (perhaps to plot experimental data points), you may want to use the plmk function -- include the library file `plmk.i' and use the help function to find out how.
The pldj command also connects points, but as disjoint line segments:
pldj, x0, y0, x1, y1
connects (x0(1),y0(1)) to (x1(1),y1(1)), then (x0(2),y0(2)) to (x1(2),y1(2)), and so on. Unlike plg, where y and x are one dimensional arrays, the four arguments to pldj may have any dimensionality, as long as all four are the same shape.
The plm command plots a quadrilateral mesh. Two dimensional arrays x and y specify the mesh. The x array holds the x coordinates of the nodes of the mesh, the y array the y coordinates. If x and y are M by N arrays, the mesh will consist of (M-1)*(N-1) quadrilateral zones. The four nodes (x(1,1),y(1,1)), (x(2,1),y(2,1)), (x(1,2),y(1,2)), and (x(2,2),y(2,2)) bound the first zone -- nodes (1,1), (2,1), (1,2), and (2,2) for short. This corner zone has two edge-sharing neighbors -- one with nodes (2,1), (3,1), (2,2), and (3,2), and the other with nodes (1,2), (2,2), (1,3), and (2,3). Most zones share edges four neighbors, one sharing each edge of the quadrilateral. The plm command:
plm, y, x
draws all (M-1)*N+M*(N-1) edges of the quadrilateral mesh. Optional keywords allow for separate color and linetype adjustments for both families of lines (those with constant first or second index).
An optional third argument is an existence map -- not all (M-1)*(N-1) zones need actually be drawn. Logically, the existence map is an (M-1) by (N-1) array of truth values, telling whether a zone exists or not. For historical reasons, plm instead requires an M by N array called ireg, where ireg(1,) and ireg(,1) (the first row and column) are all 0, the value for zones which do not exist. Furthermore, for zones which do exist, ireg can take any positive value; the value of ireg(i,j) is the "region number" (non-existent zones belong to region zero) of the zone bounded by mesh nodes (i-1,j-1), (i,j-1), (i-1,j), and (i,j). The boundary= keyword causes plm to draw only the edges which are boundaries of a single region (the region= keyword value).
As a simple example, here is how you can draw a four by four zone mesh missing its central two by two zones:
x= span(-2, 2, 5)(,-:1:5); y= transpose(x); ireg= array(0, dimsof(x)); ireg(2:5,2;5)= 1; ireg(3:4,3:4)= 0; plm, y, x, ireg;
The plc and plf commands plot functions on a quadrilateral mesh.
The plc command plots contours of a function z(x,y). If z is a two dimensional array of the same shape as the x and y mesh coordinate arrays, then
plc, z, y, x
plots contours of z. The levs= keyword specifies particular contour levels, that is, the values of z at which contours are drawn; by default you get eight equally spaced contours spanning the full range of z. Each contour is actually a set of polylines. Here is the algorithm Yorick uses to find contour polylines:
Each edge of the mesh has a z value specified at either end. For those edges with one end above and the other below the desired contour level, linearly interpolate z along the edge to find the (x,y) point on the edge where z has the level value.
Start at any such point, choose one of the two zones containing that edge, and move to the point on another edge of that zone, stepping across the new edge to the zone on its opposite side. Continue until you reach the boundary of the mesh or the point at which you started. If any points remain, repeat the process to get another disjoint contour. If you start at boundary points as long as any remain, you will first walk all open contours -- those which run from one point on the mesh boundary to another -- then all closed contours. Note that each contour level may consist of several polylines.
One additional complication can arise: Some zones might have two diagonal corners above the contour value and the two other corners below, so all four edges have points on the contour. In a sense, your mesh does not resolve this contour -- it could represent a true X-point where contour lines cross, in which case you want to move directly across the zone to the point on the opposite edge. Unfortunately, if you connect the opposite edges, you will make very ugly contour plots. (Even if you disagree on my taste in other places, trust me on this one.)
So the question is, which adjacent edge do you pick? If you don't make the same choice for every contour level you plot, contours at different levels cross (and so will your eyes when you try to interpret the picture). By default, plc will use a sort of minimum curvature algorithm: it turns the opposite direction that it turned crossing the previous zone. The triangulate= keyword to plc can be used both to force a particular triangulation of any or all zones in the mesh, and to return automatic triangulation decisions made during the course of the plc command. Again, if triangulation decisions become important to you, your mesh is probably not fine enough to resolve the contour you are trying to draw.
The plf command plots a filled mesh, that is, it gives a solid color to each quadrilateral zone in a two dimensional mesh. The color is taken from a continuous list of colors called a palette. Different colors represent different function values. A palette could be a scale of gray values from black to white, or a spectrum of colors from red to violet.
Unlike plc, for which the z array had the same shape as x and y, the z array in a plf command must be an M-1 by N-1 array if x and y are M by N. That is, there is one z value or color for each zone of the mesh instead of one z value per node:
plf, z, y, x
A separate palette command determines the sequence of colors which will represent z values. Keywords to plf determine how z will be scaled onto the palette (by default, the minimum z value will get the first color in the palette, and the maximum z the last color), and whether the edges of the zones are drawn in addition to coloring the interior. When the x and y coordinates are projections of a two dimensional surface in three dimensions, the projected mesh may overlap itself, in which case the order plf draws the zones becomes important -- at a given (x,y), you will only see the color of the last-drawn zone containing that point. The drawing order is the same as the storage order of the z array, namely (1,1), (2,1), (3,1), ..., (1,2), (2,2), (3,2), ..., (1,3), (2,3), (3,3), ...
One or two contours plotted in a contrasting color on top of a filled mesh is one of the least puzzling ways to present a function of two variables. The fill colors give a better feel for the smooth variation of the function than many contour lines, but the correspondence between color and function value is completely arbitrary. One or two contour lines solves this visual puzzle nicely, especially if drawn at one or two particularly important levels that satisfy a viewer's natural curiosity.
Digitized images are usually specified as a two dimensional array of values, assuming that these values represent colors of an array of square or rectangular pixels. By making appropriate x and y mesh arrays, you could plot such images using the plf function, but the pli command is dramatically faster and more efficient:
pli, z, x0, y0, x1, y1
plots the image with (x0,y0) as the corner nearest z(1,1) and (x1,y1) the corner nearest z(M,N), assuming z is an M by N array of image pixel values. (Currently, Yorick only plots black and white or pseudocolor images. True color images with three or more color components per pixel would require a higher dimensional z array.) The optional x0, y0, x1, y1 arguments default to 0, 0, M, N.
A third variant of the plf command is plfp, which plots an arbitrary list of filled polygons; it is not limited to quadrilaterals. While pli is a special case of plf, plfp is a generalization of plf:
plfp, z, y, x, n
Here z is the list of colors, and x and y the coordinates of the corners of the polygons. The fourth argument n is a list of the number of corners (or sides) for each successive polygon in the list. All four arguments are now one dimensional arrays; the length of z and n is the number of polygons, while the length of x and y is the total number of corners, which is sum(n). Again, plfp draws the polygons in the order of the z (or n) array.
As a special case, if all of the lengths n after the first are 1, the first polygon coordinates are taken to be in NDC units, and the remaining single points are used as offsets to plot numberof(n)-1 copies of this polygon. This arcane feature is necessary for the plmk function defined in the library file `plmk.i'.
While plc, plf, pli, and plfp plot representations of a single valued function of two variables, the plv command plots a 2D vector at each of a number of (x,y) points. The vector actually looks more like a dart -- it is an isoceles triangle with a much narrower base than height, with its altitude equal to the vector (u,v), in both magnitude and direction, and its centroid at the point (x,y):
plv, v, u, y, x
Making a good vector plot is very tricky. Not only must you find a nice looking length scale for your (u,v) vectors -- the longest should be something like the spacing between your (x,y) points -- but also you must sprinkle the (x,y) points themselves rather uniformly throughout the region of your plot. The time you spend overcoming these artistic difficulties usually isn't worth the effort.
The final plotting command, plt, plots text rather than geometrical figures:
plt, text, x, y
Optional keywords determine the font, size, color, and orientation of the characters, and the precise meaning of the coordinates x and y -- what coordinate system they are given in, and how the text is justified relative to the given point.
Unlike the other plotting primitives, by default the (x,y) coordinates in the plt command do not refer to the same (x,y) scales as your data. Instead, they are so-called normalized device coordinates, which are keyed to the sheet of paper, should you print a hardcopy of your picture. To make (x,y) refer to the so-called world coordinates of your data (what planet is your data from?), you must use the tosys=1 keyword. If you do locate text in your world coordinate system, only its position will follow your data as you zoom and pan through it; don't expect text size to grow as you zoom in, or your characters to become hideously distorted when you switch to log axis scaling.
Text may be rotated by multiples of 90 degrees by means of the orient= keyword. Arbitrary rotation angles are not supported, and the speed that rotated text is rendered on your screen may be dramatically slower than ordinary unrotated text.
You can get superscripts, subscripts, and symbol characters by means of escape sequences in the text. Yorick is not a typesetting program, and these features will not be the highest possible quality. Neither will what you see on the screen be absolutely identical to your printed hardcopy (that is never true, actually, but superscripts and subscripts are noticeably different). With those caveats, the escape feature is still quite useful.
To get a symbol character (assuming you are a font other than symbol), precede that character by an exclamation point -- for example, "!p" will be plotted as the Greek letter pi. There are four exceptions: "!!", "!^", and "!_" escape to the non-symbol characters exclamation point, caret, and underscore, respectively. And "!]" escapes to caret in the symbol font, which is the symbol for perpendicular. The exclamation point, underscore, and right bracket characters are themselves in the symbol font, and shouldn't be necessary as escaped symbols. If the last character in the text is an exclamation point, it has no special meaning; you do not need to escape a trailing exclamation point.
Caret "^" introduces superscripts and underscore "_" introduces subscripts. There are no multiple levels of superscripting; every character in the text string is either ordinary, a superscript, or a subscript. A caret switches from ordinary or subscript characters to superscript, or from superscript to ordinary. An underscore switches from ordinary or superscript characters to subscript, or from subscript back to ordinary.
If the text has multiple lines (separated by newline "\n" characters), plt will plot it in multiple lines, with each line justified according to the justify= keyword, and with the vertical justification applied to the whole block. You should always use the appropriate text justification, since the size of the text varies from one output device to another -- the size of the text you see on your screen is only approximately the size in hardcopy. In multiline text, the superscript and subscript state is reset to ordinary at the beginning of each line.
Here is an example of escape sequences:
text= "The area of a circle is !pr^2\n"+ "Einstein's field equations are G_!s!n_=8!pT_!s!n"; plt, text, .4,.7,justify="CH";
A sequence of plotting primitives only partly determines your picture. You will often want to specify the plot limits -- the range x and y values you want to see. You may also want log-log or semi-log scales instead of linear scales, or grid lines that extend all the way across your graph instead of just tick marks around the edges. Finally, you need to be able to specify the color palette used for pseudocoloring any plf, pli, or plfp primitives.
The limits, logxy, gridxy, and palette functions are the interface routines you need. If you are really fussy, you can also control the appearance of your picture in much more detail -- for example, the thickness of the tick marks, the font of the labels, or the size and shape of the plotting region. The section on graphics styles explains how to take complete control over the appearance of your graphics. This section sticks with the functions you are likely to use frequently and interactively.
There are several ways to change the plot limits: The limits command, the range command, and mouse clicks or drags. Also, the unzoom command undoes all mouse zooming operations.
The syntax of the limits command is:
limits, xmin, xmax, ymin, ymax
Each of the specified limits can be a number, the string "e" to signal the corresponding extreme value of the data, or nil to leave the limit unchanged. For example, to set the plot limits to run from 0.0 to the maximum x in the data plotted, and from the current minimum y value to the maximum y in the data, you would use:
limits, 0.0, "e", , "e"
If both xmin and xmax (or ymin and ymax) are numbers, you can put xmin greater than xmax (or ymin greater than ymax) to get a scale that increases to the right (or down) instead of the more conventional default scale increasing to the left (or up).
As a special case, limits with no arguments is the same as setting all four limits to their extreme values (rather than the no-op of leaving all four limits unchanged). Hence, if you can't see what you just plotted, a very simple way to guarantee that you'll be able to see everything in the current picture is to type:
limits
If you just want to change the x axis limits, type:
limits, xmin, xmax
As a convenience, if you just want to change the y axis limits, you can use the range function (instead of typing three consecutive commas after the limits command):
range, ymin, ymax
To zoom with using the mouse, put the mouse on the point you want to zoom around. Click the left button to zoom in on this point, or the right button to zoom out. If you drag the mouse between pressing and releasing the button, the point under the mouse when you pressed the button will scroll to the point where you release the button. The middle mouse button does not zoom, but it will scroll if you drag while pressing it. Hence, the left button zooms in, the middle button pans, and the right button zooms out.
If you click just outside the edges of the plot, near the tick marks around the edges of the plot, the zoom and pan operations will involve only the axis you click on. In this way you can zoom in on a region of x (or y) without changing the magnification in y (or x). Or with the middle button, pan along one direction without having to worry about accidentally changing the limits in the other direction slightly.
An alternative way to scroll using the mouse is to hold the shift key and press the left mouse button at one corner of the region you want to expand to fill the screen. Holding the button down, drag the mouse to the opposite corner of your rectangle, then release the button to perform the zoom. This zoom operation is more difficult to control, but it provides single step zooming with unequal x and y zoom factors. (The inverse operation -- mapping the current full screen to fill the rectangle you drag out with the mouse -- is available with shifted right button. This turns out to be unusably non-intuitive.)
After any mouse zoom function, all four limits are set to fixed values, even if they were extreme values before the zoom. You can restore the pre-zoom limits, including any extreme value settings, by typing:
unzoom
You can also invoke the limits command as a function, in which case it returns the current value of [xmin,xmax,ymin,ymax,flags]. The flags are bits that determine which of the limits (if any) were computed as extreme values of the data, and a few other optional features (to be mentioned momentarily). The value returned by limits as the argument to a later limits command restores the limits to a prior condition, including the settings of extreme value flags. Thus,
// mouse zooms here locate an interesting feature
detail1= limits()
unzoom // remove effects of mouse zooms
// mouse zooms here locate second interesting feature
detail2= limits()
limits, detail1 // look at first feature again
limits, detail2 // look at second feature again
limits // return to extreme values
The square keyword chooses any extreme limit to force the x and y scales to have identical units -- so that a circle will always look round, not elliptical, for instance. Of course, if you set all four limits explicitly, the square keyword will have no effect. For example,
limits, square=1, 0., "e", 0., "e"
forces the lower limits of both x and y to zero, while the upper limits are chosen to both show all of the data in the first quadrant, and to keep the units of x and y the same (normally, this means xmax and ymax are equal, but if the viewport were not square, xmax and ymax would be in the same ratio as the sides of the viewport). The square keyword remains in effect even if the units are changed, hence, for example,
limits
will reset all limits to extreme values, but now one axis may extend beyond the limits of the data in that direction to keep the x and y units equal. To return to the default behavior, you must explicitly call the limits command with the square keyword again, for example,
limits, square=0
You may want to choose among log, semi-log, or ordinary linear scale axes. Use the logxy command:
logxy, 1, 1 // log-log plot logxy, 1, 0 // semi-log plot, x logarithmic logxy, 0, 1 // semi-log plot, y logarithmic logxy, 0, 0 // linear axis scales
You can also omit the x or y flag to leave the scaling of that axis unchanged:
logxy, , 1
changes the y axis to a log scale, leaving the x axis scaling unchanged.
The flags returned by the limits function include the current logxy settings, if you need them in a program.
Zero or negative values in your data have no catastrophic effects with log axis scaling; Yorick takes the absolute value of your data and adds a very small offset before applying the logarithm function. As a side effect, you lose any indication of the sign of your data in a log plot. If you insist you need a way to get log axis scaling for oscillating data, write a function to treat negative points specially. For example, to draw positive-y portions solid and negative-y portions dashed, you might use a function like this:
func logplg(y, x)
{
s= sum(y>=0.); /* number of positive-y points */
n= numberof(y);
if (s) plg, max(y,0.), x, type="solid";
if (s<n) plg, min(y,0.), x, type="dash";
}
You always have the option of plotting the logarithm of a function, instead of using log axes:
logxy,0,1; plg,y,x
plots the same curve as
logxy,0,0; plg,log(y),x
The log axis scaling merely changes the position of the ticks and their labels; the y axis ticks will look like a slide rule scale in the first case, but like an ordinary ruler in the second.
The gridxy command changes the appearance of your plot in a more subjective way than logxy or limits. Ordinarily, Yorick draws tick marks resembling ruler scales around the edges of your plot. With gridxy, you can cause the major ticks to extend all the way across the middle of your plot like the lines on a sheet of graph paper. Like logxy, with gridxy you get separate control over horizontal and vertical grid lines. However, with gridxy, if you supply only one argument, both axes are affected (usually you want grid lines for neither axis, or grid lines for both axes). For example,
gridxy, 1 // full grid, like graph paper gridxy, 0, 1 // only y=const grid lines gridxy, , 0 // turn off y=const lines, x unchanged gridxy, 1, // turn on x=const lines, y unchanged gridxy, 0 // ticks only, no grid lines
If the flag value is 2 instead of 1, then instead of a full grid, only a single grid line is drawn at the "roundest number" in the range (zero if it is present). If you need a reference line at y = 0, but you don't want your graph cluttered by a complete set of grid lines, try this:
gridxy, 0, 2
The appearance of the ticks and labels is actually part of the graphics style. You will want to change other details of your graphics style far less frequently than you want to turn grid lines on or off, which explains the separate gridxy function controlling this one aspect of graphics style.
The gridxy command also accepts keyword arguments which change the default algorithm for computing tick locations, so that ticks can appear at multiples of 30. This makes axes representing degrees look better sometimes. By default, ticks can appear only at "decimal" locations, whose last non-zero digit is 1, 2, or 5. See the online help for gridxy for a more complete description of these options.
The plf, pli, and plfp commands require a color scale or palette, which is a continuum of colors to represent the continuous values of a variable. Actually, a palette consists of a finite number of (red, green, blue) triples, which represent a color for each of a finite list of values. A Yorick palette can never have more than 256 colors, so that a type char variable (one byte per item) can hold any index into a palette. Because many screens can display only 256 colors simultaneously, however, you shouldn't have more than about 200 colors in a palette; that is the size of all of Yorick's predefined palettes.
The palette command allows you to change palettes. The Yorick distribution comes with predefined palettes called `earth.gp' (the default palette), `gray.gp', `yarg.gp', `heat.gp', `stern.gp', and `rainbow.gp'. To load the gray palette, you would type:
palette, "gray.gp"
These palettes tend to start with dark colors and progress toward lighter colors. The exceptions are `yarg.gp', which is a reversed version of `gray.gp' (your picture looks like the photographic negative of the way it looks with the `gray.gp' palette), and `rainbow.gp', which runs through the colors in spectral order at nearly constant intensity. Besides `gray.gp' (or `yarg.gp') and `rainbow.gp', it's tough to find color sequences that people have been trained to think have an order. The `heat.gp' palette is a red-orange scale resembling the colors of an iron bar as it grows hotter. The default `earth.gp' is loosely based on mapmaker's colors from dark blue deep ocean to green lowlands to brown highlands to white mountains.
Instead of a file name, you may pass palette three arrays of numbers ranging from 0 to 255, which are relative intensities of red, green, and blue. For example,
scale= bytscl(indgen(200),top=255); palette, scale,scale,scale;
produces the same palette as `gray.gp', but by direct specification of RGB values, rather than by reading a palette file.
Yorick internally uses the bytscl function to map the z values in a plf, pli, or plfp command into a (0-origin) index into the palette. Occasionally, as here, you will also want to call bytscl explicitly.
The predefined palette files are in the directory Y_SITE+"gist"; you should be able to figure out their format easily if you want to produce your own. If you create a directory `~/Gist' and put your custom palette files there, Yorick will find them no matter what its current working directory. The library include file `color.i' includes functions to help you construct palettes, and a dump_palette function which writes a palette in Yorick's standard format.
You can use the query= keyword to retrieve the RGB values for the currently installed palette:
local r,g,b; palette,query=1, r,g,b;
There is also a private= keyword to palette, which you should investigate if you are interested in color table animation, or if other programs steal all your colors.
The line drawing primitives plg, pldj, plm, plc, and plv accept a color= keyword. You can use an index into the current palette, in which case the color of the line becomes dependent on the palette. However, you can also choose one of ten colors which are always defined, and which do not change when the palette changes. For example,
plg, sin(theta),cos(theta), color="red"
draws a red circle (assuming theta runs from zero to two pi). The colors you can specify in this way are: "black", "white", "red", "green", "blue", "cyan", "magenta", "yellow", "fg", and "bg".
Black and white, and the primary and secondary colors need no further explanation. But "fg" stands for foreground and "bg" for background. The default color of all curves is "fg"; the color of the blank page or screen is "bg". These colors are intentionally indefinite; unlike the others, they may differ on your screen and in print. By default, "fg" is black and "bg" is white. If you use the X window system, you can change these defaults by setting X resources. For example, set
Gist*foreground: white Gist*background: black
to make Yorick draw in reversed polarity on your screen (white lines on a black background). (The X utility xrdb is the easiest way to set X resources for your screen.)
Most graphics are overlays produced by several calls to the primitive plotting functions. For example, to compare of three calculations of, say, temperature as a function of time, type this:
plg, temp1, time1 plg, temp2, time2 plg, temp3, time3
All three curves will appear on a single plot; as you type each plg command, you see that curve appear.
The basic paradigm of Yorick's interactive graphics package is that as you type the command to add each curve to the plot, you see that curve appear. You may need to open a file or perform a calculation in order to generate the next curve. Often, an interesting comparison will occur to you only after you have seen the first few curves.
Even if you are not changing the data in a plot, you often will want to change plot limits, or see how your data looks with log or semi-log axes. You can call limits, logxy, or gridxy any time you want; the changes you request take place immediately.
Yorick maintains a display list -- a list of the primitive plotting commands you have issued to display your data -- to enable you to make these kinds of changes to your picture. The primitive functions just add items to the display list; you don't need access to the actual rendering routines. You can change secondary features like plot limits or log axis scaling changing the plotted data. In order to actually produce a picture, Yorick must "walk" its display list, sending appropriate plotting commands to some sort of graphics output device.
If the picture changes, Yorick automatically walks its display list just before pausing to wait for your next input line. Thus, while you are thinking about what to do next, you are always looking at the current state of the display list.
A command like limits does not actually force an immediate replot; it just raises a flag that tells Yorick it needs to walk its display list. Therefore, feel free to call limits or logxy several times if a Yorick program is clearer that way -- the effects will be cumulative, but you will not have to wait for Yorick to redraw your screen each time -- the replot only happens when Yorick pauses to walk the display list.
Usually, the display list paradigm does just what you expect. However, the indirection can produce some subtle and surprising effects, especially when you write a Yorick program that produces graphical output (rather than just typing plotting commands directly).
The fma command (mnemonic for frame advance) performs two functions: First, if the current display list has changed, Yorick re-walks it, ensuring that your screen is up to date. Afterwards, fma clears the display list.
In normal interactive use, you think of the first function -- making sure what you see on your screen matches the display list -- as a side effect. Your aim is to clear the display list so you can begin a fresh picture. However, in a Yorick program that is making a movie, the point is to draw the current frame of the movie, and clearing the display list is the side effect which prepares for the next movie frame.
Another side effect of fma is more subtle: Since Yorick no longer remembers the sequence of commands required to draw the screen, attempts to change plot limits (zoom or pan), rescale axes, or any other operation requiring a redraw will not change your screen after an fma, even though you may still see the picture on your screen.
Once set, limits are persistent across frame advances. That is, after an fma, the next plot will inherit the limits of the previous plot. I concede that this is not always desirable, but forgetting the previous limits can also be very annoying. Another possibility would be to provide a means for setting the initial limits for new plots; I judged this confusing. You get used to typing
fma; limits
when you know the old limits are incorrect for the new plot. The axis scaling set by logxy, and grid lines set by gridxy are also persistent across frame advances.
On rare occasions, Yorick may not update your screen properly. The redraw command erases your screen and walks the entire display list. You can also use this in Yorick programs to force the current picture to appear on your screen immediately, without the side effect of clearing the display list with fma. (Perhaps you want to start looking at a rapidly completed part of a picture while a more lengthy calculation of the rest of the picture is in progress.)
Note that redraw unconditionally walks the entire display list, while fma only walks the part that hasn't been previously walked or damaged by operations (like limits changes) that have occurred since the last walk.
You can have up to eight graphics windows simultaneously. Each window has its own display list, so you can build a picture in one window, switch to a second window for another plot, then return to the first window. The windows are numbered 0 through 7. The window command switches windows:
window, 1 // switch to window number 1 window, 0 // switch back to default window
If you switch to a window you have never used before, a new graphics window will appear on your screen. (If you do not issue an explicit window command before your first graphics command, Yorick automatically creates window number 0 for you.)
The window command takes several keywords. The wait=1 keyword pauses Yorick until the new window actually appears on your screen (if the window command creates a new window). This is important under the X window system, which cannot draw anything until a newly created window actually appears. If you write a program (such as Yorick's demo programs) which will produce graphical output, but you are not sure whether there have been any previous graphics commands, you should start with the statement:
window, wait=1;
The style= keyword to window specifies a particular graphics style for the window. I'll return to graphics styles later.
The dpi=100 keyword specifies a 100 dot per inch window, rather than the default 75 dot per inch window. The window you see on your screen is a replica of a portion of an 8.5 by 11 inch sheet of paper, and the dpi (either 75 or 100) refers to the number of screen pixels which will correspond to one inch on that sheet of paper if you were to tell Yorick to print the picture (see hcp). Owing to the fonts available with X11R4, Yorick permits only the two scale factors.
Initially, a Yorick window is a six inch by six inch window on the 8.5 by 11 sheet, centered at the center of the upper 8.5 by 8.5 inch square for portrait orientation, or at the center of the rectangular sheet for landscape orientation. (Portrait or landscape orientation is a function of the graphics style you choose. Six inches is 450 pixels at 75 dpi or 600 pixels at 100 dpi, so dpi=75 makes a small window, while dpi=100 makes a large window.) You can resize the window (with your window manager), but Yorick will always use either the 75 or 100 dot per inch scaling, so resizing a Yorick window is not very useful unless you want to use more than the six by six inch square that is initially visible.
The display= keyword specifies a display other than your default display. For example, if you have two screens you might use
window, 0 window, 1, display="zaphod:0.1"
(if zaphod is the name of the machine running your X server) in order to create window number 0 on your default screen and window number 1 on your second screen.
You will rarely need the other keywords to the window command, which allow for a private colormap (for color table animation), turn the legends off in hardcopy output, and create a private hardcopy file for the sole use of that one window. With the latter capability, you can write commands like eps that print the current picture, without affecting the main hardcopy file.
Type:
hcp
to add the picture you see on your screen to the current hardcopy file. Yorick walks the current display list, rendering to the hardcopy file instead of to your screen. To close the hardcopy file and send it to the printer, use:
hcp_out
Normally, hcp_out destroys the file after it has been printed; you can save the file in addition to printing it with:
hcp_out, keep=1
If you want to close the hardcopy file without printing it, you can call hcp_finish instead of hcp_out; if you invoke hcp_finish as a function (with a nil argument), it returns the name of the file it just closed.
After you call hcp_out or hcp_finish, the next call to hcp creates a new hardcopy file. You can call hcp_file to set the name of the next hardcopy file. For example,
hcp_file, "myfile.ps"
means that the next time Yorick creates hardcopy file, its name will be `myfile.ps'. The name of the file also implicitly determines the file type; names of the form `*.ps' will be PostScript files, while any other name will be a binary CGM. If you do not specify a file name, Yorick chooses a name will not clobber any existing file; if you specify a name and that file already exists, Yorick will silently overwrite the existing file.
When you produce a color picture (with plf or pli, for example) on your screen, you need to save not only the picture, but also the palette you used to draw that picture. Since the palette might change for each frame in a hardcopy file, the file must contain a complete palette as a sort of preamble to each frame. However, you usually want less expensive, higher quality black and white output, so the palette information is usually unnecessary. Yorick optionally converts colors to a standard gray scale before output to a hardcopy file, saving a little space in the output file.
If you are going to use a black and white printer, you can turn off palette dumping by means of the dump= keyword:
hcp_file, dump=0
(you could also specify the name for the next hardcopy file). Unlike the name of the hardcopy file, the palette dumping flag persists across any number of hardcopy files. To turn on dumping again, you need to call hcp_file with dump=1.
All Yorick PostScript files will print on any PostScript printer, black and white or color. However, if you set dump=0, the palette was not dumped to the file, and it will print gray even on a color printer. If you print a dump=1 file on a black and white printer, the result will be very similar to dump=0; your colors will be translated to grays. You can put the call to hcp_file setting dump=0 in your `custom.i' file if you rarely want to dump the palette.
A Yorick binary CGM conforms to all the recommendations of the ANSI Computer Graphics Metafile standard. Unfortunately, this standard is very outdated. Unlike PostScript, the CGM standard does not specify where the ink has to go on the page, so that every program which interprets a CGM draws a somewhat different picture. Fonts and line types vary a great deal, as does the absolute scale of the picture. The Yorick distribution includes a binary CGM browser, gist (Gist is the name of Yorick's graphics package). The gist browser can convert a binary CGM generated by Yorick into exactly the same PostScript file that Yorick would have produced. No other CGM reader can do the same.
Yorick includes CGM support for historical reasons. Unless you like using the gist browser program, you should write PostScript files directly. If you want to archive lots of pictures, you may be concerned that the PostScript file is much larger than the equivalent CGM. However, standard compression software (such as gzip from project GNU) works much better on the text of a PostScript file than on the binary CGM data, which erases most of the size discrepancy.
If you are serious about archiving pictures, you should strongly consider archiving the raw data plus the Yorick program you used to create the pictures, rather than any graphics hardcopy files. Often this is even more compact than the graphics file, and it has the huge advantage that you can later recover the actual data you plotted, making it easy to plot it differently or overlay data from other sources.
If your system includes the ghostscript program from project GNU, you can use it to make Encapsulated PostScript (EPS) files from Yorick PostScript output. Yorick's eps command will do this, although it forks Yorick in order to start ghostscript, which is a bad idea if Yorick has grown to a large size. (The gist browser which is part of the Yorick distribution can make an inferior EPS file; you can change eps to use gist if you need to.) When you see the picture you want on your screen, type:
eps, "yowza"
to create an EPS file called `yowza.epsi' containing that picture. If you don't want to fork Yorick, you can also use the hcps command, which creates a PostScript file containing just the current picture; you can then run ps2epsi by hand to convert this to an EPS file.
Page layout programs such as FrameMaker or xfig can import EPS files generated by Yorick. You can then use the page layout program to resize the Yorick graphic, place other text or graphics around it (as for a viewgraph), and even add arrows to call out features of your plot. This is the easiest way to make finished, publication quality graphics from Yorick output. You may be able to produce satisfactory results using Yorick alone, but the page layout programs will always have fancier graphical user interfaces -- they are and will remain better for page layout than Yorick.
My recommendation to set up for high quality output is this:
func hqg(onoff)
/* DOCUMENT hqg
hqg, 0
turn on and off high quality graphics output. You should
refrain from using plot or axis titles; add them with your
page layout program. You will also need to distinguish
curves by means of line type as hq turns off alphabetic
curve markers.
SEE ALSO: eps
*/
{
if (is_void(onoff) || onoff) {
window, style="vg.gs", legends=0;
pldefault, marks=0, width=4;
} else {
window, style="work.gs", legends=0;
pldefault, marks=1, width=0;
}
}
Yorick formats each page of graphics output according to a model regulated by several dozen numeric parameters. Most of these change seldom -- you tend to find a graphics style that suits you, then stick with it for many plots. Yorick demotes the stylistic parameters to a lower rank; they are intentionally harder to access and change than more urgent items like plot limits or log scaling. (The gridxy function is an exception -- it changes the stylistic parameters that determine how Yorick draws ticks, but has a relatively simple interface.)
Designing a new graphics style requires patience, hard work, and multiple iterations. Use the `*.gs' files which come with the Yorick distribution (installed in Y_SITE+"gist") as examples and starting points for your own designs. The following sections should help you to understand the meaning of the various style parameters, but I am not going to attempt to lead you through an actual design. Yorick's predefined graphics styles usually suffice.
Graphics styles are ordinarily read from files, which you load by means of the style= keyword to the window command. The Yorick distribution comes with several style files:
The `work.gs' file is the default style and my own preference. Many people prefer the `boxed.gs' style, which looks more like other graphics packages (baaa baaa). The `vg.gs' file stands for "viewgraph graphics style", which I recommend as the starting point for high quality graphics. There is a `vgbox.gs' style as well. The `axes.gs' style has coordinate axes with ticks running through the middle of the viewport, similar to the style of many elementary math textbooks. The `nobox.gs' style has no tick marks, labels, or other distractions; use it for drawing geometrical figures or imitating photographs. The `work2.gs' and `boxed2.gs' styles are variants which allow you to put a second set of tick marks and labels on the right hand side of the plot, independent of the ones on the left side. (This is a little cumbersome in Yorick, but if you really need it, that's how you're supposed get it.) Finally, `l_nobox.gs' is a variant of the nobox style in landscape orientation; all the other styles give portrait orientation.
For example,
window, style="nobox.gs"
switches to the nobox style. This clears the current display list; the new style will take effect with your next plot. If you have several different windows, they may have different styles; each window has its own style, just as it has its own display list.
The style files are in the Y_SITE+"gist" directory; the file `work.gs' in that directory contains enough comments for you to see how to write your own variants of the distribution style files. You can put variants into either that public directory, or into a private directory called `~/Gist', or in Yorick's current working directory to enable the style= keyword to find them.
If you need to control details of the graphics style from a Yorick program, you can include the library file `style.i':
#include "style.i"
This defines functions get_style and set_style which enable you to determine and change every detail of Yorick's graphics style model. Functions read_style and write_style read from or write to a text file in the format used by the style= keyword.
Most Yorick graphics styles define only a single coordinate system. In other words, there is a single transformation between the world coordinates of your data, and the normalized device coordinates (NDC) on the sheet of paper or the screen.
The world-to-NDC transformation maps the x-y limits of your data to a rectangle in NDC space, called the viewport. Normally, Yorick will not draw any part of your data lying outside the viewport. However, you can tell Yorick to use raw NDC coordinates without any transformation by means of the plsys command:
plsys, 0
tells Yorick not to transform data from subsequent plotting primitives. When you are plotting to "system 0", your data will not be clipped to the viewport. In NDC coordinates, the bottom left corner of the 8.5 by 11 sheet of paper is always (0,0), and 0.0013 NDC unit is exactly one printer's point, or 1/72.27 inch.
You can switch back to the default "system 1" plotting with:
plsys, 1
The fma command also switches you back to "system 1" as a side effect. Switching windows with the window command puts you in the coordinate system where you most recently plotted.
Remember that there is a special relation between the window on your screen and an 8.5 by 11 inch sheet of paper; the bottom left corner of the visible screen window is not usually at (0,0) in NDC. Instead, the NDC point (0.3993,0.6342) in portrait mode -- or (0.5167,0.3993) in landscape mode -- remains at the center of your screen window no matter how you resize it. (The points make more sense in inches: (4.25,6.75) -- centered horizontally and an equal distance down from the top of the page for portrait orientation, and (5.50,4.25) -- centered on the page for landscape orientation.) Furthermore, the screen scale is always 75 dpi or 100 dpi, that is, either 798.29 pixels per NDC unit or 1064.38 pixels per NDC unit.
(I apologize to European users for not making explicit provision for A4 paper. If you use Yorick's eps command for serious, high quality output, the page positioning is not an issue. For rough draft or working plots, I hope the fact that Yorick produces off-center pictures on A4 paper will not hurt you too much. An inch, incidentally, is 2.54 cm.)
You can also create a graphics style with multiple world coordinate systems. For example, you could define a style with four viewports in a single window, in a two by two array. If your style has more than one coordinate system, you use
plsys, i
to switch among them, where i can be 0, 1, 2, up to the number of coordinate systems in your style. Each coordinate system has its own limits, logxy, and gridxy parameters. However, all systems in a window must share a single palette (many screens don't have more colors than what you need for one picture anyway). The limits, logxy, and gridxy commands are meaningless in "system 0", since there is no coordinate transformation there.
For interactive use, I find multiple coordinate systems too confusing. Instead, I pop up multiple windows when I need to see several pictures simultaneously. For non-interactive use, you should consider making separate Yorick pictures, saving them with the eps command, then importing the separate pieces into a page layout program to combine them.
The style parameters regulating the location of the viewport, appearance of the tick marks, and so on are fairly straightforward: You can specify which of the four sides of the viewport will have tick marks, or you can select "axis" style, where coordinate axes and ticks run through the middle of the viewport. Tick marks can extend into or out of the viewport, or both (beware that tick marks which project into the viewport often overlay a portion of your data, making it harder to interpret). You can draw a frame line around the edge of the viewport, or just let the tick marks frame your picture. You can specify which, if any, of the four sides of the viewport will have numeric labels for the ticks, the distance from the ticks to the labels, the font and size of the labels, and so forth.
An elaborate artificial intelligence (or stupidity) algorithm determines tick and label positions. Four parameters control this algorithm: nMajor, nMinor, logAdjMajor, and logAdjMinor. Tick marks have a hierarchy of lengths. The longest I call major ticks; these are the tick marks which get a numeric label (if you turn on labels). Like the scale on a ruler, there is a hierarchy of progressively shorter ticks. Each level in the hierarchy divides the intervals between the next higher level into two or five subdivisions -- two if the interval above had a width whose least significant decimal digit of 1 or 2, five if that digit was 5.
Only two questions remain: What interval should I use for the major ticks, and how many levels should I proceed down the hierarchy? The nMajor and nMinor parameters answer these questions. They specify the upper limit of the density of the largest and smallest ticks in the hierarchy, respectively, as follows: The tick interval is the smallest interval such that the ratio of the full interval plotted to that interval is less than nMajor (or nMinor). Only major tick intervals whose widths have least significant decimal digit 1, 2, or 5 are considered (otherwise, the rest of the hierarchy algorithm fails).
Log scaling adds two twists: First, if there are any decades, I make decade ticks longer than any others, even if the first level of subdecade ticks are far enough apart to get numeric labels. If there will be ticks within each decade, I divide the decade into three subintervals -- from 1 to 2, 2 to 5, and 5 to 10 -- then use the linear scale algorithm for selecting tick intervals at the specified density within each of the three subintervals. Since the tick density changes by at most a factor of 2.5 within each subinterval, the linear algorithm works pretty well. You expect closer ticks on a log scale than you see on a linear scale, so I multiply the nMajor and nMinor densities by logAdjMajor and logAdjMinor before using them for the subintervals.
A final caveat: On the horizontal axis, long numeric labels can overlap each other if the tick density gets too high. This depends on the font size and the maximum number of digits you allow for those labels, both of which are style parameters. Designing a graphics style which avoids this evil is not easy.
Yorick allows you to query the current display list, and to edit parameters such as line types, widths, and the like. I usually choose to simply make a new picture with corrected values; the existing low level editing command is more suited to be the basis for a higher level menu driven editing system.
Every plotting primitive function accepts a legend= keyword to allow you to set a legend string describing that object. If you do not supply a legend= keyword, Yorick supplies a default by repeating a portion of the command line. For example,
plg, cos(x), x
will have the default legend "A: plg, cos(x), x", assuming that the curve marker for this curve is "A". You can specify a more descriptive legend with the legend= keyword:
plg, cos(x), x, legend="oscillating driving force"
If you want the legend to have the curve marker prepended, so it is "A: oscillating driving force" if the curve marker is "A", but "F: oscillating driving force" if the curve marker is "F", you can begin the legend string with the special character "\1":
plg, cos(x), x, legend="\1: oscillating driving force"
Like legends, you can specify a curve marker letter with the marker= keyword, but if you don't, Yorick picks a value based on how many curves have been plotted. By default, Yorick draws the marker letter on top of the curve every once in a while -- so A's mark curve A, B's mark curve B, and so on. This is only relevant for the plg and plc commands. This default style is ugly; use it for working plots, not polished graphics. You should turn the markers off by means of the marks=0 keyword for high quality plots, and distinguish your curves by line type. For example,
plg, cos(x), x, marks=0, type="dash", legend="oscillating driving force (dash)"
In order to conserve screen space, legends never appear on your screen; they only appear in hardcopy files. Furthermore, depending on the graphics style, legends may not appear in hardcopy either. In particular, the `vg.gs' and `nobox.gs' styles have no legends. This is because legends are ugly. Legends take the place of a proper figure caption in working plots. For high quality output, I expect you to take the trouble to add a proper caption. You can use the legends=0 keyword to the window command in order to eliminate the legends even from those graphics styles where they normally appear.
Any time you want to see your legends, you can type:
plq
For any plotting primitive you have issued, you can find all its keyword values by specifying its index in the plq list (the basic plq command prints this index):
plq, 3
For the plc command, each contour level is a polyline; you can find out about each individual level by adding a second index argument to plq.
You can also invoke plq as a function in order to return representations of the current display list elements for use by a Yorick program.
Just as plq allows you query the current display list, pledit allows you to edit it. You specify an index as in the plq command (or two indices for a specific contour level), then one or more keywords that you want to change. This is occasionally useful for changing a line type or width. One could build a more user friendly interface combining plq and pledit with some sort of menu or dialogue.
With the pldefault command, you can set default values for many of the keywords in the window command and the primitive plotting commands. These keywords control the line type, width, and color for the plg, plc, plm, and pldj commands, the style and dpi of newly created windows, and other properties of graphics windows and objects.
You can use pldefault interactively to set new, but temporary, default values before embarking on a series of plots. Or place a pldefault command in your `~/Yorick/custom.i' file if your personal preferences differ from Yorick's defaults.
You must use the hcp_file command to set values for the dump= or ps= keywords (which determine whether to dump the palette to hardcopy files and whether automatically created hardcopy files are PostScript or binary CGM), and these settings remain in effect until you explicitly change them.
For example, if you want to use the `vg.gs' style, with unmarked wide lines, and you want PostScript hardcopy files (by default) including the palette for each frame, you can put the following two lines in your `custom.i' file:
pldefault, style="vg.gs", marks=0, width=4; hcp_file, dump=1, ps=1;
The values you set using pldefault are true default values -- you can override them on each separate call to window or a plotting primitive, but the default value remains in effect unless you reset it with a second call to pldefault. Conversely, you can't temporarily override a dump= or ps= value you set with hcp_file -- you just have to set them to a new value.
You may want to plot something not directly supported by one of the plotting primitive functions. Usually, you can write your own plotting function to perform such a task. As an example, suppose you want to plot a histogram -- that is, instead of a smooth curve connecting a series of (x,y) points, you want to draw a line consisting of a horizontal segment at each value of y, joined by vertical segments (this is sometimes called a Manhattan plot for its resemblance to the New York City skyline).
You quickly realize that to draw a histogram, you really need the x values at the vertical segments. This function works:
func plh(y,x)
{
yy= xx= array(0.0, 2*numberof(y));
yy(1:-1:2)= yy(2:0:2)= y;
xx(2:-2:2)= xx(3:-1:2)= x(2:-1);
xx(1)= x(1);
xx(0)= x(0);
plg, yy, xx;
}
Notice that the x array must have one more element that the y array; otherwise the assignment operations to xx will fail for lack of conformability.
A more sophistocated version would include the possibility for the caller to pass plh the keywords accepted by the plg function. Also, you might want to allow y to have one more element than x instead of x one more than y, in order to start and end the plot with a vertical segment instead of a horizontal segment. Here is a more complete version of plh:
func plh(y,x,marks=,color=,type=,width=)
/* DOCUMENT plh, y, x
plot a histogram (Manhattan plot) of Y versus X. That is,
the result of a plh is a set of horizontal segments at the Y
values connected by vertical segments at the X values. If X
has one more element than Y, the plot will begin and end with
a horizontal segment; if Y has one more element than X, the
plot will begin and end with a vertical segment. The keywords
are a subset of those for plg.
KEYWORDS: marks, color, type, width
SEE ALSO: plg
*/
{
swap= numberof(x)<numberof(y);
if (swap) { yy= y; y= x; x= yy; }
yy= xx= array(0.0, 2*min(numberof(y),numberof(x)));
yy(1:-1:2)= yy(2:0:2)= y;
xx(2:-2:2)= xx(3:-1:2)= x(2:-1);
xx(1)= x(1);
xx(0)= x(0);
if (swap) { y= yy; yy= xx; xx= y }
plg, yy, xx, marks=marks, color=color, type=type, width=width;
}
The point of an interpreted language is to allow you to easily alter the user interface to suit your own needs. Designing an interface for a wide variety of users is much harder than designing one for your own use. (The first version of plh might be adequate for your own use; I wouldn't release less than the second version to a larger public.) Linking your routines to the Yorick help command via a document comment is useful even if never anticipate anyone other than yourself will use them. Public interface routines should always have a document comment.
Yorick's ordinary drawing commands make poor movies. In order to make a good movie on your screen, you need to use more resources -- the idea is to draw the picture into offscreen memory first, then use a fast copy operation to make the entire frame appear instantly.
The animate command creates an offscreen pixmap, and redirects the rendering routines there, and modifies the fma command to perform the copy from offscreen to onscreen. A second animate command returns to the ordinary mode of operation.
You can't use animate mode for ordinary interactive work, because you don't get to see your picture until the fma. Typically, you need to write a Yorick function which enters animate mode, loops producing frames of the movie, then exits animate mode. Use the high level movie interface by including
#include "movie.i"
After this include, you can use the movie function, and let it manage the low level animate function for you. You only need to write a function (passed as an argument to movie) that draws the n-th frame of your movie and returns a flag saying whether there are any more frames. You can easily build and test this function a frame at a time in ordinary non-animation mode. Study the `demo2.i', `demo3.i', and `demo5.i' demonstration programs that come with the Yorick distribution to learn more about making movies.
You can combine Yorick's graphical primitives to create your own graphics interfaces. The Yorick distribution includes a simple 3D graphics interface built in just this way. Study the `pl3d.i' library file if you need an example of how to do this.
Before you can access the 3D functions, you need to include either `plwf.i' or `slice3.i', which provide the highest level 3D interfaces. The `plwf.i' interface lets you plot scalar functions of two variables (like plc or plf). The `slice3.i' interface allows you to plot isosurfaces and slices of scalar functions of three variables. The `demo5.i' demonstration program in the Yorick distribution has examples of both; study it to see how they work in detail.
Since the picture you are viewing is always two dimensional, you need to project three dimensional objects onto your screen. I designed the 3D interface to allow you to change this projective mapping without re-issuing plotting commands, by analogy with the 2D limits command.
Instead of limits, however, you need to specify a projection. Imagine that your screen is the image plane of a camera. This camera carries a coordinate system in which +x is rightward, +y is upward, and +z is out of the screen toward your face. Your world coordinate system -- the coordinates of your data -- has some orientation relative to these camera coordinates. You can rotate your world coordinates -- and see the object you have plotted rotate -- using either the rot3 or orient3 commands.
The three arguments to rot3 are the angles (in radians) to rotate your object about the camera x, y, and z axes. These three rotations are applied in order, first the x angle, then the y angle, then the z angle. Omitted or nil arguments are the same as zero arguments. Note that 3D rotation is not a commutative operation, so if you want the z rotation to come first, followed by an x rotation, you need to issue two rot3 commands (like limits or logxy changes, rot3 changes accumulate so Yorick will only redraw your screen once):
rot3,,,zangle; rot3,xangle
The orient3 operation is less general than rot3, and therefore easier to use. With orient3, the z axis of your world coordinates always projects parallel to the camera y axis, so z is plotted upward on your screen. The first argument to orient3 is the angle about the world z axis that the world x axis should be rotated relative to the camera x axis. The optional second argument to orient3 is the angle from the camera y to the world z -- the angle at which you are looking down at the scene. Once set, that downlook angle persists until you reset it. With no arguments, orient3 returns to a sane standard orientation. Hence, you can just type:
orient3
to return to a standard view of your scene.
Unlike rot3, the results of an orient3 are not cumulative. With each orient3 call, you completely specify the orientation of your object. Thus, if you set the orientation of your object by orient3:
orient3, -.75*pi
and you want to twist it just a little, you would issue a slightly different orient3 command:
orient3, -.70*pi
While if your rot3 command
rot3,, -.75*pi
were not quite right, you'd twist it a little more by specifying a small angle:
rot3,, .05*pi
Unfortunately, the projection operation is more complicated than merely the three angles of an arbitrary rotation matrix. The distance from your camera (or eye) to the object also enters. By default, you are infinitely far away, looking through an infinite magnification lens. This is called isometric projection, because equal length parallel segments in your world coordinates have equal length on your screen, no matter how close or far from the camera. If you want a perspective picture, you can use the setz3 command to set the z value in the camera coordinate system where your camera (or eye) is located.
Don't bother attempting to put the camera inside your data; it's not edifying. Extreme fisheye perspectives won't make your scientific data more intelligible. Keep the camera well outside your object.
Your camera always looks toward -z. Initially, the aim point is the origin of the camera coordinate system, but you can change it to any other point by means of the aim3 function. If you want to specify an aim point in your world coordinate system instead, use the mov3 function, which is analogous to rot3.
In general, you should use the nobox.gs graphics style, since the 2D axes are not useful. For an isometric view, the 2D (x,y) axes are simple projections of your (x,y,z) world coordinates. For a perspective view (after you have called setz3), the 2D (x,y) coordinates are the tangents of ray angles between the camera z axis and points on your object. The 2D limits command is useful for cropping or adjusting your view, but you should not turn off the square=1 keyword.
The functions save3 and restore3 save and later restore the entire 3D coordinate mapping, including the effects of rot3 or orient3, mov3, aim3, and setz3. You can also undo the effects of any number of these commands by means of the undo3 function.
Yorick's pseudocolor model cannot describe a shaded color object. (You need to be able to specify arbitrary colors for that.) Therefore, if you want shading to suggest a 3D shape, you must settle for black and white (or any single color -- the `heat.gp' palette works about as well as `gray.gp' for shading).
You also need to worry about more variables -- namely the position and relative brightness of any lights illuminating your object. You call the light3 function to set up light sources.
The 3D surfaces comprising your object (plotted by either plf or plfp for the `plwf.i' or `slice3.i' interfaces) consist of a number of polygonal facets. Yorick assigns a 3D normal vector to each facet (for non-planar polygons, the direction is somewhat arbitrary). The lighting model, which you set up by light3 calls, maps normal directions to surface brightness. If the facet is oriented so that it reflects one of your light sources directly toward your camera, that facet will appear very bright.
The functional form of this specular reflection model is (1+cos(alpha))^n, where alpha is the angle between your camera and the light source, as seen from the center of the facet. You can adjust the power n. Large n gives a polished metallic look; small n gives a matte look. You can specify as many light sources as you want. However, the light sources are all infinitely far away. Also, although you can specify a different n for each light source, you cannot get dfferent values of n for different surfaces or parts of surfaces.
Instead of or in addition to these specular light sources, you can also get diffuse lighting. In this model, the brightness of a surface is simply proportional to the cosine of its angle relative to your viewing direction. Thus, a surface you view face on is brightest, while one viewed edge on is dimmest (the effect is called limb darkening).
All of these effects are controlled by keywords to the light3 function: specular (brightness of specular light sources), sdir (directions to specular light sources), spower (the powers n for the specular light sources), diffuse (brightness of diffuse light source), and ambient (an overall additive brightness). The light3 function also returns a value, which you can pass back to it in a later call in order to restore a previous lighting specification.
These lighting models -- which are actually the standard models you find in most 3D graphics packages -- bear little relationship to the appearance of real world scenes. There are no shadows, and surfaces have no intrinsic texture. Your goal is excitement, not realism.
Use the gnomon function to turn the gnomon on or off. The gnomon is a projection of three orthgonal segments parallel to the world x, y, and z axes. The letters x, y, and z appear near the ends of the segments -- a black on white letter means the segment is pointing out of the screen; white on black means into the screen.
A related function is cage3, which turns 3D axes on or off. The cage is a rectangular box surrounding your object, with tick marks on its edges to make your plot a 3D analogue of a typical 2D plot. (Of course, unlike the 2D plot, there is no way you can actually use the ticks around the cage to figure out the coordinates of a point in your picture.) The faces of the cage are at planes set by the limit3 function. The plwf function automatically calls limit3 with reasonable extreme values, but for the pl3tree function you will need to call limit3 yourself. Unlike the 2D limits function, limit3 allows you to set the aspect ratio of the cage; by default cage3 scales your x, y, and z world coordinates to make the edges of the cage appear as equal lengths.
A plwf plot contains the same information as a plf or plc plot; a plwf is like looking at terrain from a mountain peak, while plf or plc is like looking at a map. (Have you ever stood at a vista point, and used a map to identify and understand the "real" scene you are seeing? Would you rather have a photograph of the vista or a map to guide you through unfamiliar terrain?)
Like plc, plwf needs point centered z values on a quadrilateral mesh. A scale factor relating the units of z to the units of x or y may be specified via keyword; if not, plwf chooses a scale factor to make the range of z values half of the larger of the ranges of x and y. Other keywords allow you to choose whether or not the mesh lines will be drawn, and whether the surface itself will be shaded according to the scene lighting, or simply left the background color. (The latter choice results in a wire frame plot, which is the wf in plwf.)
The slice3 function, in contrast, makes planar slices or isosurface contours of functions of three variables. You can pseudocolor the planar slices, or show projections of the isosurfaces, or combine slices with isosurfaces. The plfp function makes the actual plots, but you call a higher level function, pl3tree.
Unlike plwf, you must invoke at least two functions in order to plot anything. First, you call to slice3 returns either a planar slice through your 3D data, or an isosurface of some scalar function on your 3D mesh. Next, you call pl3tree in order to add the slice or isosurface to your 3D display list.
Again, Yorick's pseudocolor model does not permit partially transparent surfaces. Often, you need to slice open closed isosurfaces like a melon in order to view the interior. Use the slice2 and slice2x functions to make slices like this. Unfortunately, when you remove slices to make interior surfaces visible, you will only be able to see through your slice from a restricted range of viewing directions. That is, the best slicing planes will depend on your viewing direction. Study the `demo5.i' for an example of how to make this type of picture.
A trick allows you to both color your slices (like a 2D plf or pli), and shade your isosurfaces, despite the limitations of the pseudocolor model. The idea is to split your palette into a gray scale you can use to shade isosurfaces, followed by a color palette you can use to pseudocolor slices. The split_palette routine in `slice3.i' generates such a split palette. Again, read `demo5.i' for details.