Go to the previous, next section.
There is limited support for 2-dimensional graphics in SM,
and we do not expect to make many extensions in this area. Currently it is
possible to read an unformatted image, to draw contours, to extract values
from the image into vectors, and to obtain image values with a cursor.
Previous editions of this manual (prior to version 2.3) have traditionally
said: `We expect to support half-tone imaging in the nearish future';
as of this release there is a macro (called greyscale
and written
by Gabor Toth here at Princeton) that draws reasonably efficient
grey scale images on any supported output device.
The problem of specifying data formats for images is difficult, and we have
adopted an approach of using a `filecap' file to describe the unformatted
files. This allows the user to write the file using C or Fortran (or,
presumably, lisp) and then use SM to read the data.
The name of the entry in the filecap
file is given by the
variable file_type
, which may be given in your `.sm' file
if you use the standard startup
macro.
The format of data on disk is first the x- and y- dimensions of the
image, then the data in
row-ascending order. The exact statements used to write the data may depend
on the chosen value of file_type
(or vice-versa). A description
of the filecap
fields comes at the end of this appendix; most
users should never have to change this file.
It is also possible to read data from a formatted file and create an image
inside SM, for example if x
and y
are integers in the
range 0-9:
IMAGE (10,10) # declare a 10*10 array READ { x 1 y 2 vals 3 2 # read some data SET IMAGE(x,y) = vals # and put it into the image
The command used to read an image is IMAGE, and you may optionally
specify the x and y range covered by the data
(e.g. IMAGE datafile xmin xmax ymin ymax
; this also works with
declarations like the one in the previous paragraph). Only the region of the
image lying within the current limits is plotted when contouring - just
like any other graphics operation in SM. Images may be deleted
with the DELETE IMAGE command. To extract values from an image,
use the special expression IMAGE(vec_x,vec_y)
which has the
value of the image at the points (vec_x,vec_y).
You can extract variables from the image's header using the command
DEFINE name IMAGE
. See the discussion of filecap if you need
to know what variables can be retrieved this way.
If you want to use non-interactive SM (i.e. write your own command
interpreter), there is a call (defimage
) to define your data array
to the contouring package, but this is of course not available interactively.
The current filecap is as follows:
# # Filecap describes unformatted file formats to SM. The syntax is # identical to termcap or graphcap files. # c|C|C files:\ :HS#8:nx#0:ny#4: ch|CH|C files with headers:\ :HS#24:nx#0:ny#4:x0#8:x1#12:y0#16:y1#20: fits|cfits|FITS|CFITS|C FITS files:\ :DA=fits:RL=2880: no_header|Files with no header, will prompt for nx, ny:\ :HS#0: unix|UNIX|Fortran unformatted files under Unix on a Vax:\ :HS#-1:nx#0:ny#4:RS#4:RL#-1:RE#4: unix_int|UNIX_INT|Like unix, but integer*4:\ :DA=int:tc=unix: unix_short|UNIX_SHORT|Like unix, but integer*2:\ :DA=short:tc=unix: vms_var|VMS_VAR|Fortran unformatted under VMS, recordtype=variable:\ :HS#8:nx#0:ny#4: vms_fixed|VMS_FIXED|Fortran unformatted under VMS, recordtype=fixed:\ :HS#-1:RS#0:RL#-1:REnx#0:ny#4: # This seems to be correct, based on one example vms_direct:VMS_DIRECT|Fortran unformatted, direct access, under vms:\ :HS#8:RS#0:RE#0:nx#0:nx#4:Comment lines start with a #, continuation lines start with white space (a tab or a space) and \ may be used to continue an entry on to the next line. The first few fields in an entry are separated by
|
, and are alternative
names for the same entry. For example,
fortran unformatted files under Unix may be referred to as unix
or
UNIX
. Fields are separated by colons, and are of the form
:CC#nnn:
for numbers, and :SS=str:
for strings. Omitted fields
may be specified as :CC@nnn:
, or simply omitted.
Filecap capabilities currently used are:
DA (DAta type)
FS (File Start)
HS (Header Size)
RE (Record End)
NS (No Swap)
RL (Record Length)
RS (Record Start)
SW (SWap)
nx (Number X)
ny (Number Y)
x0 (X 0)
x1 (X 1)
y0 (Y 0)
y1 (Y 1)
HH
is supported as an archaic form of HS.
In terms of these quantities a file will look like this:
As mentioned below, HS
can be negative and is then taken as the record
length of the header RL_H
, in which case the file will be
like:
Note that the first real data record begins with an RS
-- this means
that
if you are writing fortran you must write the header in a
different write statement than the one that you use to write the data
(i.e. write(fd) nx,ny,arr
will not work).
Most parameters are optional, and will default to 0. If RE or RS is specified, you must give RL as well. If you specify it as -ve, then we'll look for it from the operating system. You must provide a value for HS (or HH if you're old fashioned), if it is negative we'll assume that even the header has a record structure, with RS and RE just like any other record. Its record length will be taken to be RL, if RL is positive, otherwise we'll find it from the operating system. If HS and RL are both negative there is no reason why the record length of the header should be the same as that of the data.
If neither nx nor ny
are present in the graphcap entry you will
be prompted@footnote #{in fact they will be read from a macro
or the command line without prompting if they are available; try
define file_type no_header image file \n 10 20
}
for the x and y dimensions of the file, otherwise they
must both be present.
If they are negative
they are taken to be the negation of the true dimension (so a filecap entry
:ny#-6:
specifies that the y-dimension of the data is 6), but usually
they are taken as the offsets of the values of the x and y dimensions in
the file, relative to the start of the header (if you set HS to be zero
you can specify nx and ny on the command line, but they must be on a
separate line, either a real separate line, or following a \n). Note
that HS excludes
FS, so HS will usually be 2*sizeof(int) irrespective of the value of
FS, and nx will usually be 0. If HS is negative, then the nx and ny
offsets also ignore RS,
i.e. nx is still usually 0.
As an alternative to specifying the actual file sizes as negative
integer values of nx or ny (e.g. :nx#-10:
) you can simply specify
them as positive string values: :nx=10:
. Note how this differs
from :nx#6:
.
Possibilities for DA
are char
, float
(default),
int
,
long
, and short
, all as in C, and also fits
for FITS
format data. (If you don't know what FITS format is, don't worry about
it. It's a style of header and record structure used for data
transport in astronomy. If you do know about FITS, then we assume that
each record is 2880by long, although possibly with RS and RE non-zero.
The header is processed for the size of the file and the value of
BITPIX. If the file is not SIMPLE, it is taken to be 4by floating
point data. CRPIX, CRVAL, and CDELT
are interpreted correctly, as are BZERO and BSCALE. X0, x1, y0,
and y1 are specified as the keywords X0, X1, Y0, and Y1 respectively,
and take preference over CRPIX etc.
FITS files on a machine with vax byte order are supposed to be byte
swapped, but you can override this by specifying the NS
capability
which stops SM from doing any byte swapping).
If you specify SW
it takes preference over any other instructions
about byte swapping and forces byte-swapping for 2byte (short) data,
and byte-and-word swapping for 4byte (int) data.
If x0
and x1
, or y0
and y1
are omitted,
the range of
values on the appropriate axis is taken to be 0 to nx-1 (or ny-1).
You can override these values with the IMAGE
command.
The values of X0
(and so on) can be obtained directly using
DEFINE X0 IMAGE
; Other values can be specified in `filecap',
for example a filecap entry :aa#24:
specifies that
DEFINE aa IMAGE
should recover the (floating-point) number stored
at byte offset 24. Because of the way that filecap files work, you are
restricted to two-character names.
If you are using FITS files, all the keywords from the header are
available, but you should remember that SM is case sensitive.
For example, suppose I wrote a file using the Unix f77 compiler, with some code that looked like:
integer *4 nx,ny,arr(10,20) c nx = 10 ny = 20 write(8) nx,ny write(8) arr(Omitting opening unit 8 as an unformatted file, and filling the array with data). Then I could read it in SM by defining
file_type
to be unix_int. The filecap entry indicates that the length of the header
is to be obtained from unix (in fact from the file, it'll be the
record length of the header), as is the record length.
Both the start-of-record (RS) and end-of-record (RE) gaps are 4by
long (they in fact contain the record length, but you needn't know that).
The number of x-records is at zero offset in the file,
and y-records is at offset 4. In other words, allowing for FS being 0 and
RS being 4,
nx occupies bytes 4-7 in the file, and ny 8-11.
The data is taken to be 4-byte integers (integer*4 to fortran).
In fact, the first record consists of only the values of
nx and ny, so the record length of the first record is 8by, and part
of an equivalent filecap entry would be@br
:FS#4:HS#12:where I have interpreted RS as FS, and added the RE onto the end of the header length HS. If I had written the data out line-by-line in a do loop, the file type would still be unix_int, but the record length actually used by SM in reading the file would be different (@xref{Image}).
As another example, I use an image processing system called Wolf that used to use files with the arcane structure of 200bytes of header, then the x- and y-size of the file, given as ints, then more header up to a total offset of 1024 bytes from the start of the file, then the data written as short integers. I could write a header with code that looked somewhat like (omitting all error checking):
int fd,i; int xs = 20,ys = 10; short arr[10][20]; ... lseek(fd,200L,0); write(fd,(char *)&xs,sizeof(int); write(fd,(char *)&ys,sizeof(int); lseek(fd,1024L,0); for(i = 0;i < ys;i++) write(fd,(char *)arr[i],xs*sizeof(short));The corresponding filecap entry is
wolf|Wolf-IfA files:\ :HS#1024:nx#200:ny#204:DA=short:which is pretty simple really.
Go to the previous, next section.