home *** CD-ROM | disk | FTP | other *** search
- /* GNUPLOT - apollo.trm */
- /*
- Apollo terminal driver for GNUplot.
-
- Open a pad for the graphics, and use GPR routines. For additional
- speed, we do the graphics to a separate bitmap, and the blt the
- entire bitmap to the display. When the user specifies an output
- file name, however, we draw directly to the screen, so the graphics
- are written to the file correctly. Thus, the user can save the
- graphics in a file, to be viewed later. If we try the bitmap
- trick, it looks funny.
-
- Ray Lischner (uunet!mntgfx!lisch)
- 4 October 1989 file created for GNUplot 1.1
- 26 March 1990 updated for GNUplot 2.0
- */
-
- #include <apollo/base.h>
- #include <apollo/error.h>
- #include <apollo/pad.h>
- #include <apollo/gpr.h>
-
- /* default tick sizes for small windows */
- #define APOLLO_VTIC 6
- #define APOLLO_HTIC 6
-
- /* issue an error message, using additional text "s" */
- #define apollo_error(s) error_$print_name(status, (s), strlen(s))
-
- /* if "status" indicates an error, then issue an error message */
- #define apollo_check(s) if (status.all != status_$ok) apollo_error(s)
-
- static ios_$id_t stream = -1; /* the stream for the pad */
- static gpr_$bitmap_desc_t screen_desc; /* the screen's bitmap */
- static gpr_$bitmap_desc_t bitmap_desc; /* the graphics bitmap */
- static gpr_$attribute_desc_t attr; /* attribute block for saved bitmap */
- static short draw_width; /* default GPR draw width */
- static name_$long_pname_t font_name; /* font path name */
- static int APOLLO_XMAX, APOLLO_YMAX; /* window size */
- static boolean use_bitmap; /* use a separate bitmap? */
-
- /* return whether stdout is a DM pad */
- apollo_isa_pad()
- {
- status_$t status;
- pad_$isa(1, &status);
- return (status.all == status_$ok);
- }
-
- /*
- Find out what the default font is for the pad, and save the
- character height and width information.
-
- Note that we must save the font file name because we need
- to reload the font file everytime the window changes size.
- */
- static void apollo_font_info(struct termentry* tbl, char* fname)
- {
- short fwidth, fheight, flen;
- status_$t status;
-
- /* get the font size & update the termentry table */
- pad_$inq_font(stream, &fwidth, &fheight, fname, name_$long_pnamlen_max,
- &flen, &status);
- apollo_check("inq_font");
- fname[flen] = '\0';
-
- tbl->v_char = fheight;
- tbl->h_char = fwidth;
- }
-
- /*
- Initialize all the GPR stuff. To save time, we draw into a separate
- bitmap, and then blt it onto the screen all at once. This results
- in 5-10 times speed-up in the graphics, with only a little
- complication. Most of the complication is right here, making sure
- we allocate the right bitmaps, etc., in the right order. The rest
- is in APOLLO_text(), where we actually BLT the bitmap onto the screen.
- Everything else is the same.
-
- The bitmaps have the same size as the window. If the window changes
- size, then the bitmaps retain the same size, so the user sees part
- of the plot or a lot of space around the plot. Drawing a new plot,
- or replotting the previous one causes APOLLO_graphics() to see if
- the window has changed size, in which case the GPR is terminated,
- and this routine is called again. Thus, make sure any changes
- preserve this ability. Anything that should only be done once
- to the pad should be handled by APOLLO_init().
-
- By the way, we save the current draw width, to be used later
- for drawing extra wide lines. This way we don't need to know
- anything about the current output device characteristics;
- we can just draw the default width, or twice the default width, etc.
- */
- static void apollo_gpr_init(struct termentry* tbl, pad_$window_desc_t* window)
- {
- gpr_$offset_t size;
- short fontid;
- status_$t status;
-
- size.x_size = APOLLO_XMAX = tbl->xmax = window->width;
- size.y_size = APOLLO_YMAX = tbl->ymax = window->height;
-
- /* now initialize GPR */
- gpr_$init(gpr_$frame, stream, size, 1, &screen_desc, &status);
- apollo_check("gpr_$init");
-
- if (use_bitmap)
- {
- /* allocate the bitmap and its attribute block */
- gpr_$allocate_attribute_block(&attr, &status);
- apollo_check("allocate_attribute_block");
-
- gpr_$allocate_bitmap(size, 1, attr, &bitmap_desc, &status);
- apollo_check("allocate_bitmap");
-
- gpr_$set_bitmap(bitmap_desc, &status);
- apollo_check("set_bitmap");
- }
-
- /* set the font file */
- gpr_$load_font_file(font_name, strlen(font_name), &fontid, &status);
- apollo_check(font_name);
-
- gpr_$set_text_font(fontid, &status);
- apollo_check("set_text_font");
-
- gpr_$inq_draw_width(&draw_width, &status);
- apollo_check("inq_draw_width");
- }
-
- /*
- Determine the tick sizes to be used for labelling borders.
- By default, we use 1/50 of the window size, which looks nice to me.
- If this makes the ticks too small, however, we use a minimum
- size, to make sure they are visible. The minimum size was also
- determined experimentally.
-
- Feel free to changes the sizes to something you feel looks better.
-
- This routine must be called after apollo_gpr_init(), because we
- need to know the window size, as stored in the termentry table.
- */
- static void apollo_tic_sizes(struct termentry* tbl)
- {
- /* base the tick size on the window size */
- tbl->v_tic = tbl->ymax / 50;
- if (tbl->v_tic < APOLLO_VTIC)
- tbl->v_tic = APOLLO_VTIC;
- tbl->h_tic = tbl->xmax / 50;
- if (tbl->v_tic < APOLLO_HTIC)
- tbl->v_tic = APOLLO_HTIC;
- }
-
- /*
- Terminate the GPR. This is called whenever the window size
- changes, and we need to reinitialize the GPR. I assume that
- calling gpr_$terminate() also deallocates the bitmaps and
- attribute blocks because deallocating the screen's bitmap
- causes terminate() to think GPR has already been terminated.
-
- Since this can be called many times, make sure nothing
- drastic is done here, like closing the stream to the pad.
- The only actions should be those that will be reinitialized
- by apollo_gpr_init().
- */
- static void apollo_gpr_terminate()
- {
- status_$t status;
-
- gpr_$terminate(false, &status);
- apollo_check("terminate");
- }
-
- /*
- Initialize the graphics. This is called once, so we do things
- here that should be done exactly once, such as opening the window.
- I like to give windows names, so it is obvious what the window's
- contents are, but this causes a transcript to be kept in a file
- whose name is the window's name. This might be nice in some
- circumstances, but to me it is a nuisance, so the file is
- deleted immediately. The name is unlikely to appear normally,
- so there should be little interference with users' normal files.
- If the user has explicitly set the output file, however, then
- we use that name, and do not delete the file. Thus, the
- user can get a metafile of the plot. We can tell if the
- output file has been set because outstr is "STDOUT". Otherwise,
- outstr is the filename, in single quotes. We need to strip
- the quotes to make the file name.
-
- The DM defaults are used for window sizes and positions. If
- the user doesn't like it, he or she can change is and issue
- a replot command (assuming a plot has already been generated).
-
- Note, also, that we must call pad_$set_scale() or else
- pad_$inq_windows() returns scaled values, which is not what
- we want. Setting the scale to one (1) turns off the scaling,
- so we get real pixel sizes.
-
- Finally, we get the name and size of the default font. The
- name is kept, as explained earlier. Then we can initialize
- the GPR stuff.
-
- Note that there is a way that APOLLO_init() gets called more
- than once. If the user issues the "set terminal apollo" command
- more than once, then this is called, so we need to make sure
- that we do not keep creating windows.
-
- An alternative strategy would be to interpret multiple "set term
- apollo"s to mean create multiple windows. The user could only
- access the most recent window because GNUplot has no concept of
- multiple terminals. The user would, in fact, have no way of
- removing old windows because they are still active. We could try
- catching keyboard events to see if the user presses EXIT, but I do
- not want to start getting into that mess. If the user really
- wants this kind of functionality, then he or she can run gnuplot
- multiple times. I think that is a lot cleaner, don't you?
- */
- APOLLO_init()
- {
- /* only initialize once */
- if (stream == -1)
- {
- extern char outstr[];
- struct termentry* tbl;
- pad_$window_desc_t window;
- name_$long_name_t wname;
- short wnum; /* junk needed by pad_$inq_windows() */
- boolean unlink_wname;
- status_$t status;
-
- tbl = &term_tbl[term];
-
- /* make the window name unique, with "gnuplot" in the label */
- if (strcmp(outstr, "STDOUT") == 0)
- {
- sprintf(wname, "gnuplot-%d", getpid());
- unlink_wname = true;
- }
- else
- {
- /* strip the single quotes around the file name */
- strcpy(wname, outstr + 1);
- wname[strlen(wname) - 1] = '\0';
- unlink_wname = false;
- }
-
- use_bitmap = unlink_wname;
-
- /* use the default window position and size */
- window.top = window.left = window.width = window.height = 0;
- pad_$create_window(wname, strlen(wname), pad_$transcript, 1, window,
- &stream, &status);
- apollo_check("create_window");
-
- /* if this is not the user's name, then delete the file */
- if (unlink_wname)
- unlink(wname);
-
- /* remove all scaling, to revert to pixel units, not char. units */
- pad_$set_scale(stream, 1, 1, &status);
- apollo_check("set_scale");
-
- /* get rid of the window when the program exits */
- pad_$set_auto_close(stream, 1, true, &status);
- apollo_check("set_auto_close");
-
- /* now determine the window size & update the termentry table */
- pad_$inq_windows(stream, &window, 1, &wnum, &status);
- apollo_check("inq_windows");
-
- /* the order of the next three calls is important */
- apollo_font_info(tbl, font_name);
- apollo_gpr_init(tbl, &window);
- apollo_tic_sizes(tbl);
- }
- }
-
- /*
- Prepare for graphics output. Since this is what the user wants to
- do when preparing a new plot, this is a meaningful time to see if
- the window has changed size. Thus, we avoid mucking about with
- asynchronous traps, and we avoid the bigger problem of dealing
- with a half-finished plot when the window changes size.
-
- Simply put, get the current window size, and if it has changed,
- then get rid of the old bitmaps, etc., and allocate new ones at
- the new size. We also need to update the termentry table.
- If the window stays the same size, then just clear it.
- */
- static void apollo_redo_window(pad_$window_desc_t* window)
- {
- struct termentry* tbl = &term_tbl[term];
- status_$t status;
-
- /* the order of the following calls is important */
- apollo_gpr_terminate();
- apollo_gpr_init(tbl, window);
- apollo_tic_sizes(tbl);
- }
-
- APOLLO_graphics()
- {
- pad_$window_desc_t window;
- short wnum;
- status_$t status;
-
- pad_$inq_windows(stream, &window, 1, &wnum, &status);
- apollo_check("inq_windows");
-
- if (window.width != APOLLO_XMAX || window.height != APOLLO_YMAX)
- apollo_redo_window(&window);
- else
- {
- gpr_$clear(0, &status);
- apollo_check("clear");
- }
- }
-
- /* set a line type:
- -2 heavy, solid (border)
- -1 heavy, dotted (axis)
- 0 solid (normal)
- 1 dots (other curves)
- 2 short dash
- 3 long dash
- 4 dash dot
-
- Apparently, GPUplot draws a lot of short line segments, and each
- one starts a new pattern. This makes the patterns somewhat useless,
- but one can still tell the difference between solid, dotted, and
- dashed lines. The utility of fancier styles is limited, however.
-
- On a color workstation, we should use different colors, but I
- don't have one.
- */
-
- /*
- To draw different line styles on an Apollo, we use two different
- parameters. One is a line thickness, which is just an integral
- multiple of the default line thickness. The second is a 16-bit
- pattern that is repeated. We could use fancier patterns, since
- GPR supports up to 64-bits, but, as I explained earlier, this
- really does not buy us anything.
-
- I used patterns that do not start with all bits on because
- GNUplot seems to use lots of short line segments to draw
- a curve, and this might make a very curvey plot seem like
- a solid line, regardless of pattern. I don't want to start
- with too many zeros, however, or else the curve might not
- appear at all! All the patterns, therefore, start with one
- bit on. The rest of the bits determine the true pattern.
-
- By examining graphics.c, we see that linetype -2 is used exclusively
- for the border, -1 for the axes, and the non-negative integers for
- the curves. We use heavy lines for the border and axes, and normal
- width lines for the curves.
-
- Since C arrays start at zero, make sure all the offsets are correct,
- so that it is easy to access the array with -2...n linetypes.
- */
-
- typedef struct {
- short width;
- short pattern;
- } APOLLO_LINE;
-
- static APOLLO_LINE apollo_lines[] = {
- { 2, ~0 }, /* heavy, solid */
- { 2, 0x6666 }, /* heavy, dotted */
- { 1, ~0 }, /* normal */
- { 1, 0xAAAA }, /* dotted */
- { 1, 0xC3C3 }, /* short dash */
- { 1, 0xE01F }, /* long dash */
- { 1, 0x87F8 }, /* dash dot */
- { 1, 0x6666 }, /* big dots */
- };
-
- #define BITS_PER_LINETYPE 16
-
- /* apollo_line(-2) is the border style, etc. */
- #define apollo_line(x) apollo_lines[(x)+2]
- #define apollo_pattern(x) &apollo_line(x).pattern
- #define apollo_width(x) apollo_line(x).width
-
- #define APOLLO_MIN_LINE (-2)
- #define APOLLO_MAX_LINE (sizeof(apollo_lines)/sizeof(*apollo_lines)-2)
-
- /* set the line style */
- APOLLO_linetype(ltype)
- int ltype;
- {
- status_$t status;
-
- if (ltype < APOLLO_MIN_LINE)
- ltype = APOLLO_MIN_LINE;
- if (ltype >= APOLLO_MAX_LINE)
- ltype %= APOLLO_MAX_LINE;
-
- gpr_$set_line_pattern(1, apollo_pattern(ltype), BITS_PER_LINETYPE, &status);
- apollo_check("set_line_pattern");
-
- gpr_$set_draw_width(draw_width * apollo_width(ltype), &status);
- apollo_check("set_draw_width");
- }
-
- /* issue an error message that includes an (x, y) coordinate */
- static void apollo_xy_error(char* s, int x, int y, status_$t status)
- {
- char buffer[128];
-
- sprintf(buffer, "%s(%d, %d)", s, x, y);
- apollo_error(buffer);
- }
-
- #define apollo_xy_check(s) \
- if (status.all != status_$ok) apollo_xy_error((s), x, y, status)
-
- /*
- Note that GNUplot and GPR have reversed ideas of where the Y origin is.
- This means subtracting the Y coordinate from Y max.
- */
- #define plot_to_gpr(y) (APOLLO_YMAX - (y))
-
- /* move to a new position */
- APOLLO_move(unsigned int x, unsigned int y)
- {
- status_$t status;
-
- gpr_$move((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
- apollo_xy_check("move");
- }
-
- /* draw a line to a new position */
- APOLLO_vector(unsigned int x, unsigned int y)
- {
- status_$t status;
-
- gpr_$line((gpr_$coordinate_t) x, plot_to_gpr(y), &status);
- apollo_xy_check("line");
- }
-
- /*
- On terminals, this switches to text mode. The real meaning,
- however, is that the graphics are finished. This means we can
- now display the saved bitmap.
- */
- APOLLO_text()
- {
- if (use_bitmap)
- {
- static gpr_$position_t pos; /* always zero */
- gpr_$window_t window;
- status_$t status;
-
- /* bitblt the entire bitmap to the entire window */
- window.window_base.x_coord = 0;
- window.window_base.y_coord = 0;
- window.window_size.x_size = APOLLO_XMAX;
- window.window_size.y_size = APOLLO_YMAX;
-
- gpr_$set_bitmap(screen_desc, &status);
- apollo_check("set_bitmap(screen_desc)");
-
- gpr_$pixel_blt(bitmap_desc, window, pos, &status);
- apollo_check("bitblt");
-
- gpr_$set_bitmap(bitmap_desc, &status);
- apollo_check("set_bitmap(bitmap_desc)");
- }
- }
-
- APOLLO_text_angle(ang)
- int ang;
- {
- status_$t status;
-
- gpr_$set_text_path(ang ? gpr_$up : gpr_$right, &status);
- apollo_check("set_text_path");
- return TRUE;
- }
-
- static enum JUSTIFY apollo_text_mode;
-
- APOLLO_justify_text(mode)
- enum JUSTIFY mode;
- {
- apollo_text_mode = mode;
- return TRUE;
- }
-
- /*
- Write "str" right justified on row "row". A row is assumed to
- have whatever height the current text has. Make sure the
- text does not cover the tick marks.
- */
- APOLLO_put_text(x, y, str)
- unsigned int x, y;
- char str[];
- {
- gpr_$offset_t size;
- status_$t status;
-
- gpr_$inq_text_extent(str, strlen(str), &size, &status);
- apollo_check("inq_text_extent");
-
- y -= size.y_size / 2; /* center around "y" */
- switch (apollo_text_mode)
- {
- case LEFT:
- break;
- case CENTRE:
- x -= size.x_size / 2;
- break;
- case RIGHT:
- x -= size.x_size;
- break;
- }
- APOLLO_move(x, y);
-
- gpr_$text(str, strlen(str), &status);
- apollo_check("put_text");
- }
-
- /* reset the graphics state and terminate */
- APOLLO_reset()
- {
- if (stream != -1)
- {
- apollo_gpr_terminate();
- stream = -1;
- }
- }
-