/* Tools for generating Postscript graphics files. */ /* Last edited on 2004-04-10 22:37:08 by stolfi */ #ifndef pswr_H #define pswr_H /* This module provides tools to simplify the generation of a sequence of pictures with similar dimensions, such as animation frames, alternative designs, etc.. The figure stream can be written as a single file containing a multipage Postscript document, or a sequence of files each containing one Encapsulated Postscript figure. In either case, each page/figure may contain multiple pictures, arranged in a rectangular array in occidental reading order. The tools in this package keep track of the next available slot in the current page/figure, and automatically issue the appropriate Postscript commands when a new picture is started --- such as reposition the plot window, start a new page, close the current file and open a new one, etc. */ #include #include #include typedef enum { HOR = 0, VER = 1 } PSAxis; /* POSTCRIPT STREAM OBJECT */ typedef PSStreamPrivate PSStream; /* A PSStream is an opaque object that can be used to generate Postscript (PS) files containing a sequence of graphics and/or text pictures. The graphics operations below, such as {pswr_segment}, write the appropriate Postscript language commands to the underlying file(s). The output files may be of two types, `encapsulated' (EPS) and `standalone' (non-EPS). An EPS file contains a single figure, of arbitrary size. It has a "%%BoundingBox" line at the beginning, no "%%Page" lines, and no explicit "showpage" command. It is usually meant to be included in other documents. Some printers and viewers may not be prepared to handle an isolated EPS file. In what follows, we assume that an EPS file contains a single ``page,'' whose dimensions are given by the "%%BoundingBox". A standalone (non-EPS) file contains a complete document meant to be printed on paper of some standard size, such as "letter" or "a4". A standalone PS file may contain multiple pages; it has a "%%Page" structuring comment before each page, explicit "showpage" commands at the end of each page, and (usually) no "%%BoundingBox" specification. It is usually meant to be printed by itself, and cannot be easily included as a figure of some other document. A {PSStream} can be configured to produce either kind of file. After creating a {PSStream} of the desired type, the client need only specify the start of each new picture, without having to worry about its layout. The {PSStream} routines will combine those pictures into pages, and then write each page as either a new page of a standalone PS file, or as a separate EPS file with a systematicaly-generated name. In general, the pictures are arranged within a page in a rectangular array, with suitable margins, separation, and space for captions. */ PSStream *pswr_new_stream ( bool eps, /* TRUE for EPS figures, FALSE for PS document. */ char *name, /* Document name or figure name prefix. */ FILE *file, /* Optional open file handle. */ char *paperSize, /* Paper size ({letter}, {a3}, etc.). */ double hPageSize, /* Total figure width (in pt). */ double vPageSize /* Total figure height (in pt). */ ); /* Allocates a new {PSStream} record and initializes it appropriately. If {eps} is false, the pictures will be written to the given {file}, or, if {file} is NULL, to new file called "{name}.ps". Either way, the file will be a single standalone printable document, possibly with multiple pages numbered from 1. The page dimensions are defined by the {paperSize} string (see {pswr_get_paper_dimensions} below); the arguments {hPageSize,vPageSize} are used only if {paperSize} is NULL or empty. If {eps} is TRUE, the pictures will be written out as a sequence of files called "{name}-{NNNNNN}.eps", one per logical page, where {NNNNNN} is a six-dgit page number starting from 1. However, if the {file} argument is not NULL, then the first logical page will be written to it. The {paperSize} string is ignored, and the EPS bounding box size is set to {hPageSize} by {vPageSize}. The default picture layout for a new stream is one picture per page, using the whole page area except for margins and separators. In standalone (non-EPS) files, the margins are 1 inch wide all around the page, plus extra space for 5 lines of caption (50pt) under the picture. In EPS files, the margin is 4pt all around the figure, and there is no extra space for caption. */ void pswr_close_stream(PSStream *ps); /* Terminates any pictures that have been written to {ps}, flushs and closes any open files. Then frees all internal storage used by {ps}. */ /* PAGE LAYOUT */ void pswr_set_page_size ( PSStream *ps, /* Picture stream. */ double hPageSize, /* Width of page (in pt). */ double vPageSize /* Height of page (in pt). */ ); /* If {ps} is an EPS stream, changes the page size (i.e. the figure size) to the given dimensions in pt. The current page, if any, is closed. If {ps} is a standalone document, the operation has no effect. */ void pswr_set_page_layout ( PSStream *ps, /* Picture stream. */ double hPicSize, /* Width of each picture (in pt). */ double vPicSize, /* Height of each picture (in pt). */ bool adjustPicSize, /* TRUE to fit {hPicSize,vPicSize} to page size. */ double hPicMargin, /* Left/right margin for each picture (in pt). */ double vPicMargin, /* Top/bottom margin for each picture (in pt). */ int captionLines, /* Number of caption lines below each picture. */ int hCount, /* Number of pictures in each row. */ int vCount /* Number of pictures in each column. */ ); /* Changes the number and layout of pictures per page/figure. After this call, each page will contain {vCount} rows of pictures, with {hCount} pictures each. Each picture will occupy a rectangle with proportions {hPicSize × vPicSize}. It will be surrounded by a blank margin with dimensions {hPicMargin,vPicMargin}, and below the picture there will be additional space for {captionLines} lines of caption (at 10pt for line). If {adjustPicSize} is FALSE, then {hPicSize} and {vPicSize} are assumed to be actual dimensions in pt. If {adjustPicSize} is true, these parameters will be automatically scaled, by the same factor, so that the whole picture array fits as snugly as possible in the page/figure bounding box, with a margin of 1 inch for documents, or 4pt for encapsulated figures. Similarly, any picture count ({hCount} and/or {vCount}) which is zero is computed automatically according to this same criterion. If any pictures have already been started in the current page/figure, the latter is flushed, and a new page/figure is started. */ void pswr_new_page(PSStream *ps, const char *pageName); /* Starts a new logical page: either a document page or a new encapsulated figure. If the latter, closes the current file (if any) and opens a new one. The plot window is reset to the whole page. For EPS streams, the new file is called "{streamName}-{pageName}.eps". where {streamName} is the {name} argument given to {pswr_new_stream}. If {pageName} is NULL or empty, it defaults to the six-digit page number (starting from "000000"). For non-EPS streams, the {pageName} string is used as the first field of the "%%Page" structuring comment. If NULL or empty, it defaults to the page number, (starting from "0"). */ void pswr_new_picture ( PSStream *ps, /* Postscript picture stream. */ double xMin, double xMax, /* Client X plotting range. */ double yMin, double yMax /* Client Y plotting range. */ ); /* Signals the start of a new picture. The plot window is set to the next available picture slot in the current virtual page. A new page is started if necessary. Regardless of the {eps} flag, the plotting scale parameters are set up so that the rectangle {[xmin _ xmax] \x [ymin _ ymax]} in client coordinates will fit centered in the selected slot (excluding its margins), with equal scale factors on both axes. */ void pswr_sync_page(PSStream *ps, const char *pageName); /* Like {pswr_new_page} if any {pswr_new_picture} or {pswr_set_window} has been issued to the current page. Otherwise it is a no-op. */ void pswr_fill_row(PSStream *ps); /* Marks all picture slots in the current row as full, so that the next call to {pswr_new_picture} will start in the next row (possibly in the next page). */ void pswr_fill_page(PSStream *ps); /* Marks all picture slots in the current page as full, so that the next call to {pswr_new_picture} will start in a new page. */ /* PLOTTING WINDOW */ void pswr_set_window ( PSStream *ps, double xmin, double xmax, double ymin, double ymax, double hmin, double hmax, double vmin, double vmax, int xn, int yn ); /* Sets the plot window to an arbitrary rectangle in the current page, ignoring the picture layout. Also marks all slots in the current page full, so that the next {} will start a new page. After this call, client coordinates will range over {[xmin _ xmax] x [ymin _ ymax]}. The nominal plotting area will be {[hmin _ hmax] x [vmin _ vmax]}, in pt, relative to the lower left corner of the figure (EPS) or the current page (non-EPS). The plotting scales {dh/dx} and {dv/dy} must be equal. The caption cursor will be positioned to the first text line under the window, aligned with its left margin. Except for {pswr_add_caption} and {pswr_frame}, all graphics commands will be clipped to the plotting window. The plot window is implicitly divided into a grid of rectangular cells, with {xn} columns and {yn} rows. These cells are used by {pswr_grid_lines} and {pswr_grid_cell} below. */ void pswr_get_paper_dimensions(const char *papersize, double *xpt, double *ypt); /* Sets *xpt and *ypt to the dimensions of the specified paper type, in points. Knows about US sizes "letter", "ledger", "tabloid", "legal", "executive", and the ISO "A" sizes (from "4A0" to "A10"). */ /* DRAWING COMMANDS */ void pswr_set_pen ( PSStream *ps, double R, double G, double B, double width, double dashlength, double dashspace ); /* Sets pen parameters and ink color for line/outline drawing. Dimensions are in *millimeters* */ void pswr_segment ( PSStream *ps, double xa, double ya, double xb, double yb ); /* Draws segment from {(xa,ya)} to {(xb,yb)}. */ void pswr_tick ( PSStream *ps, PSAxis axis, double xc, double yc, double ticksz, double align ); /* Draws a tick mark (short segment) at coordinates {(xc,yc)}. The segment will be perpendicular to the given {axis} and its length will be {ticksz} millimeters, irrespective of the current scale) The segment will extend {align*ticksz} mm in the negative direction, and {(1-align)*ticksz} mm in the positive direction. */ void pswr_curve ( PSStream *ps, double xa, double ya, double xb, double yb, double xc, double yc, double xd, double yd ); /* Draws a Bezier arc with given control points. */ /* AXES, TICK MARKS, ETC. */ void pswr_coord_line (PSStream *ps, PSAxis axis, double pos); /* Draws a reference line PERPENDICULAR to the given axis at the given coordinate value, extending across the whole plot window. */ void pswr_axis(PSStream *ps, PSAxis axis, double pos, double lo, double hi); /* Draws an arrow spanning the given cordinate interval parallel to the given axis, and positioned at coordinate {pos} on the opposite axis. */ void pswr_ticks ( PSStream *ps, PSAxis axis, double lo, double hi, int n, char *fmt, double ticksz, double align ); /* Draws {n+1} coordinate ticks on the given axis, from coordinate {lo} to coordinate {hi}. If {fmt} is not null, also writes the coordinate value, with that format. The ticks have length {ticksz} (in mm) and extend from {-align*ticksz} to {(1-align)*ticksz} in the direction perpedicular to the given {axis}. */ void pswr_grid_lines(PSStream *ps); /* Draws all grid cell boundaries with the current pen and color. */ void pswr_frame (PSStream *ps); /* Draws the outline of the current plotting area. The outline will be drawn half-inside, half-outside the area. */ /* FIGURE FILLING & DRAWING COMMANDS */ /* For all commands of this section, if the {fill} argument is true, the specified figure is painted with the current fill color; then, if {draw} is true, the outline of the figure is drawn with the current pen parameters. However, if the {R} component of the current fill color is negative, the color is considered "invisible", and the shapes will not be filled, irrespective of the {fill} argument. The fill color is initially 50% gray and is reset to that value at every new page. */ void pswr_set_fill_color(PSStream *ps, double R, double G, double B); /* Defines the fill color for subsequent filling operations on {ps}. If {R} is negative, the color is interpreted as "invisible". */ void pswr_rectangle ( PSStream *ps, double xlo, double xhi, double ylo, double yhi, bool fill, bool draw ); /* Fills and/or outlines the given rectangle. */ void pswr_triangle ( PSStream *ps, double xa, double ya, double xb, double yb, double xc, double yc, bool fill, bool draw ); /* Fills and/or outlines the triangle with corners {a,b,c}. */ void pswr_polygon ( PSStream *ps, double x[], double y[], int npoints, bool fill, bool draw ); /* Fills and/or outlines the polygon {(x[1],y[1]),.. (x[n],y[n])}. */ void pswr_circle ( PSStream *ps, double xc, double yc, double rad, bool fill, bool draw ); /* Fills and/or outlines the circle with given center and radius. */ void pswr_dot ( PSStream *ps, double xc, double yc, double rad, bool fill, bool draw ); /* Same as {pswr_circle}, except that the radius is in millimeters, irrespective of the current scale. */ void pswr_arrowhead ( PSStream *ps, double xa, double ya, double xb, double yb, double width, double length, double fraction, bool fill, bool draw ); /* Fills and/or outlines a triangular head for an arrow with base at {a = (xa,ya)} and tip at {b = (xb,yb)}. The head will have the specified {width} and {length} and its tip will be positioned at the given {fraction} of the way from {a} to {b}. */ void pswr_lune ( PSStream *ps, double xc, double yc, double rad, double tilt, bool fill, bool draw ); /* Fills and/or outlines the lune with given center, radius, and tilt. (A "lune" is the intersection of two circles with the given radius, and with their centers spaced {rad} apart.) */ void pswr_slice ( PSStream *ps, double xc, double yc, double rad, double start, double stop, bool fill, bool draw ); /* Fills and/or outlines the pie slice with given center, radius, and angle range (in degrees). */ /* GRID CELLS */ void pswr_grid_cell ( PSStream *ps, int xi, int yi, bool fill, bool draw ); /* Fills and/or outlines cell {[xi,yy]} of the curent grid. Cell {[0,0]} lies at the bottom left corner. */ /* TEXT PRINTING */ void pswr_set_label_font(PSStream *ps, const char *font, double size); /* Sets the name and point size of the font to be used by pswr_label. */ void pswr_label ( PSStream *ps, const char *text, double x, double y, double xalign, double yalign ); /* Prints {label} at point {(x,y)}, using the current label font size. The parameter {xalign} (resp. {yalign}) specifies which point of the string's bounding box will end up at {(x,y)}: 0.0 means the left (resp. bottom) side, 1.0 means the right (resp. top) side. Default is (0.5, 0.5), meaning the box will be centered at {(x,y)}. */ void pswr_add_caption(PSStream *ps, const char *txt, double xalign); /* If {ps.captionLines} is zero, does nothing. If {ps.captionLines} is positive, adds a line of caption text below the current plot window, *outside* the nominal bounding box. Successive calls append succesive lines to the caption, even if the stated limit {ps.captionLines} is exceeded. Newlines in {txt} are honored. Each line is aligned as specified by {xalign}: 0.0 = left aligned, 0.5 = centered, 1.0 = right aligned. */ /* MISCELLANEOUS */ void pswr_comment (PSStream *ps, const char *title); /* Writes a Postscript comment line to the underlying file. It is intended for documentation and debugging purposes only; it has no visible effect on the printed page. */ double pswr_round_to_nice(double x); /* Rounds the absolute valeu of {x} up to a nice value (namely 0.25, 0.50, or 1.00 times a power of 10). The sign is preserved. If {x} is already one of those nice values (or too close to one), it should be rounded up to the next one. */ #endif