www.pudn.com > geosteiner-3.1.zip > genps.c


/***********************************************************************

	File:	genps.c
	Rev:	b-1
	Date:	02/28/2001

	Copyright (c) 1993, 2001 by David M. Warme

************************************************************************

	Routines for outputting stuff.

************************************************************************

	Modification Log:

	a-1:	04/18/93	warme
		: Created.  Moved most output stuff into this file
		:  from other places.
	b-1:	02/28/2001	warme
		: Changes for 3.1 release.  Pass scaling info
		:  explicitly to these routines.  Added plot_subtour.
		:  Always define X and Y scales to be equal.
		:  Fix format in non-geometric case of plot_lp_solution.

************************************************************************/

#include "genps.h"
#include "steiner.h"


/*
 * Global Routines
 */

void		begin_plot (enum plot_size);
void		define_Plot_Terminals (struct pset *, struct scale_info *);
void		draw_segment (struct point *,
			      struct point *,
			      struct scale_info *);
void		end_plot (char *);
void		overlay_plot_subset (char *,
				     bitmap_t *,
				     struct cinfo *,
				     enum plot_size);
void		plot_full_sets (bitmap_t *, struct cinfo *, enum plot_size);
void		plot_full_sets_grouped (bitmap_t *,
					struct cinfo *,
					enum plot_size);
void		plot_lp_solution (char *,
				  double *,
				  struct cinfo *,
				  enum plot_size);
void		plot_subtour (char *,
			      double *,
			      struct cinfo *,
			      bitmap_t *,
			      enum plot_size);


/*
 * External References
 */

	/* none */


/*
 * Local Equates
 */

#define	SMALL_PLOTS_PER_PAGE	12


/*
 * Local Routines
 */

static void		announce_new_page (void);
static void		define_coordinate_axes (struct pset *,
						struct scale_info *);
static void		draw_efst (struct full_set *, struct cinfo *);
static void		draw_fst (struct full_set *, struct cinfo *);
static void		draw_rfst (struct full_set *, struct cinfo *);
static void		draw_fractional_fst (struct full_set *,
					     double,
					     struct cinfo *);
static void		fst_comment (struct full_set *);
static double		get_limit (double);
static void		page_break (void);


/*
 * Local Variables
 */

static int		current_page = 0;
static enum plot_size	current_plot_size;
static int		small_plots_on_page = 0;

/*
 * This routine emits Postscript that defines the coordinates of
 * all the terminals.  The "N DefineTerminals" procedure causes space
 * to be allocated for N terminals.  We then emit the terminals, one
 * per line with "X Y DT" procedure calls.  The "DT" procedure simply
 * stuffs the given X,Y coordinates into the next slot in the TermX and
 * TermY arrays.
 *
 * Once the terminals are defined, the X,Y coordinates of a terminal (e.g.,
 * terminal 57) can be pushed onto the Postscript stack using "57 T".
 */

	void
define_Plot_Terminals (

struct pset *		pts,		/* IN - terminals to plot */
struct scale_info *	sip		/* IN - problem scaling info */
)
{
int			i;
int			n;
struct point *		p1;
char			buf1 [32];
char			buf2 [32];

	if (pts EQ NULL) return;

	n = pts -> n;

	printf ("\n%%%%BeginSetup\n");

	define_coordinate_axes (pts, sip);

	printf ("\n%d DefineTerminals\n", n);

	p1 = &(pts -> a [0]);
	for (i = 0; i < n; i++, p1++) {
		coord_to_string (buf1, p1 -> x, sip);
		coord_to_string (buf2, p1 -> y, sip);
		(void) printf ("\t%s\t%s\tDT\n", buf1, buf2);
	}
	printf ("\n%%%%EndSetup\n\n");
}

/*
 * This routine determines appropriate ranges for the X and Y coordinate
 * axes, and emits the corresponding Postscript definitions for the
 * MinX, MaxX, MinY and MaxY variables.
 */

	static
	void
define_coordinate_axes (

struct pset *		pts,		/* IN - terminals to plot */
struct scale_info *	sip		/* IN - problem scaling info */
)
{
int			i;
int			n;
struct point *		p1;
double			x, y;
double			minxcoord, maxxcoord;
double			minycoord, maxycoord;
double			xspan, yspan, span;
double			axmin, axmax;
double			aymin, aymax;

	n = pts -> n;

	if (n < 1) {
		printf ("\n0 1 0 1 SetAxes\n");
		return;
	}

	p1 = &(pts -> a [0]);

	minxcoord = maxxcoord = p1 -> x;
	minycoord = maxycoord = p1 -> y;
	++p1;

	for (i = 1; i < n; i++, p1++) {
		x = p1 -> x;
		y = p1 -> y;
		if (x < minxcoord) {
			minxcoord = x;
		}
		else if (x > maxxcoord) {
			maxxcoord = x;
		}
		if (y < minycoord) {
			minycoord = y;
		}
		else if (y > maxycoord) {
			maxycoord = y;
		}
	}

	minxcoord = UNSCALE (minxcoord, sip);
	maxxcoord = UNSCALE (maxxcoord, sip);
	minycoord = UNSCALE (minycoord, sip);
	maxycoord = UNSCALE (maxycoord, sip);

	/* We only generate square plots having equal scales on both	*/
	/* axes.  Determine the "span" of the plot, i.e., the length of	*/
	/* each axis in the plot.					*/

	xspan = maxxcoord - minxcoord;
	yspan = maxycoord - minycoord;

	if (xspan EQ 0.0) {
		if (yspan EQ 0.0) {
			/* Single point. */
			if (maxxcoord NE 0.0) {
				if (fabs (maxxcoord) >= fabs (maxycoord)) {
					span = 2.0 * fabs (maxxcoord);
				}
				else {
					span = 2.0 * fabs (maxycoord);
				}
			}
			else if (maxycoord NE 0.0) {
				span = 2.0 * fabs (maxycoord);
			}
			else {
				/* Single point at the origin. */
				span = 2.0;
			}
		}
		else {
			span = get_limit (yspan);
		}
	}
	else if (yspan EQ 0.0) {
		span = get_limit (xspan);
	}
	else if (xspan >= yspan) {
		span = get_limit (xspan);
	}
	else {
		span = get_limit (yspan);
	}

	/* Determine the minimum x axis value. */

	if (xspan EQ 0.0) {
		goto center_x;
	}
	else if ((0.0 <= minxcoord) AND (maxxcoord <= span)) {
		axmin = 0.0;
	}
	else if ((-span <= minxcoord) AND (maxxcoord <= 0.0)) {
		axmin = -span;
	}
	else if ((-0.5 * span <= minxcoord) AND (maxxcoord <= 0.5 * span)) {
		axmin = -0.5 * span;
	}
	else {
center_x:
		/* Center the x coordinates. */
		axmin = 0.5 * (minxcoord + maxxcoord - span);
	}
	axmax = axmin + span;

	/* Determine the minimum y axis value. */

	if (yspan EQ 0.0) {
		goto center_y;
	}
	else if ((0.0 <= minycoord) AND (maxycoord <= span)) {
		aymin = 0.0;
	}
	else if ((-span <= minycoord) AND (maxycoord <= 0.0)) {
		aymin = -span;
	}
	else if ((-0.5 * span <= minycoord) AND (maxycoord <= 0.5 * span)) {
		aymin = -0.5 * span;
	}
	else {
center_y:
		/* Center the y coordinates */
		aymin = 0.5 * (minycoord + maxycoord - span);
	}
	aymax = aymin + span;

	/* Good enough for now... */
	printf ("\n%g %g %g %g SetAxes\n", axmin, axmax, aymin, aymax);
}

/*
 * This routine returns a reasonable scale limit for the given quantity.
 * These are always 1, 2 or 5 times a power of 10.
 */

	static
	double
get_limit (

double		value		/* IN - value to get scale limit for */
)
{
int		i;
double		limit;

	if (value >= 1.0) {
		limit = 1.0;
		for (i = 0; i <= 20; i++) {
			if (limit >= value) return (limit);
			if (2.0 * limit >= value) return (2.0 * limit);
			if (5.0 * limit >= value) return (5.0 * limit);
			limit *= 10.0;
		}
		return (value);
	}

	limit = 1.0;
	for (i = 0; i <= 20; i++) {
		if (0.5 < value * limit) return (1.0 / limit);
		limit *= 10.0;
		if (2.0 < value * limit) return (5.0 / limit);
		if (1.0 < value * limit) return (2.0 / limit);
	}
	return (value);
}

/*
 * This routine generates some Postscript commands to plot each full-set
 * in the given mask.
 */

	void
plot_full_sets (

bitmap_t *		fset_mask,	/* IN - subset of full-sets to plot */
struct cinfo *		cip,		/* IN - compatibility info */
enum plot_size		plot_size	/* IN - size of plot to produce */
)
{
int			i;
int			n;
struct full_set *	fsp;
int *			vp1;
int *			vp2;
char			title [256];
char			buf1 [32];

	n = cip -> num_edges;

	if ((cip -> pts NE NULL) AND (cip -> full_trees NE NULL)) {
		/* Draw the FSTs with postscript. */
		for (i = 0; i < n; i++) {
			if (NOT BITON (fset_mask, i)) continue;
			fsp = cip -> full_trees [i];
			begin_plot (plot_size);
			(void) printf ("\tPlot_Terminals\n");
			fst_comment (fsp);
			draw_fst (fsp, cip);
			dist_to_string (buf1,
					cip -> cost [i],
					&(cip -> scale));
			(void) sprintf (title, "FST %lu,  Length = %s",
					(int32u) i, buf1);
			end_plot (title);
		}
		page_break ();
	}
	else {
		/* Just print the hyperedges */
		for (i = 0; i < n; i++) {
			if (NOT BITON (fset_mask, i)) continue;
			printf ("Edge %d: ", i);
			vp1 = cip -> edge [i];
			vp2 = cip -> edge [i + 1];
			while (vp1 < vp2) {
				printf ("%d ", *vp1++);
			}
			dist_to_string (buf1,
					cip -> cost [i],
					&(cip -> scale));
			printf ("%s\n", buf1);
		}
	}
}

/*
 * This routine generates some Postscript commands to plot each full-set
 * in the given mask.  Note that we "group" as many mutually-disjoint
 * full sets as possible on each plot so as to minimize the amount of
 * paper we spew...
 */

	void
plot_full_sets_grouped (

bitmap_t *		fset_mask,	/* IN - subset of full-sets to plot */
struct cinfo *		cip,		/* IN - compatibility info */
enum plot_size		plot_size	/* IN - size of plot to produce */
)
{
int			i;
int			j;
int			n;
int			nleft;
int			kmasks;
int			nmasks;
int			nplot;
bitmap_t *		left;
bitmap_t *		tmask;
int *			plist;
struct full_set *	fsp;
struct pset *		pts;
char *			cp1;
char *			cp2;
char			fsnums [110];
char			fsnum [8];
char			title [120];

	n = cip -> num_edges;
	nmasks = cip -> num_edge_masks;
	kmasks = cip -> num_vert_masks;

	left = NEWA (nmasks, bitmap_t);
	tmask = NEWA (kmasks, bitmap_t);
	plist = NEWA (n, int);

	/* Make a local copy of all full sets left to plot. */
	for (i = 0; i < nmasks; i++) {
		left [i] = fset_mask [i];
	}
	nleft = n;

	while (nleft > 0) {
		begin_plot (plot_size);

		for (i = 0; i < kmasks; i++) {
			tmask [i] = 0;
		}

		nplot = 0;
		cp1 = &fsnums [sizeof (fsnums)];
		*--cp1 = '\0';

		for (i = n - 1; i >= 0; i--) {
			if (NOT BITON (left, i)) continue;

			/* Skip full set "i" if not disjoint	*/
			/* with all others in this plot...	*/
			fsp = cip -> full_trees [i];
			pts = fsp -> terminals;
			for (j = 0; j < pts -> n; j++) {
				if (BITON (tmask, pts -> a [j].pnum)) break;
			}
			if (j < pts -> n) continue;

			(void) sprintf (fsnum, "%lu", (int32u) i);
			for (cp2 = fsnum; *cp2 NE '\0'; cp2++) {
			}

			/* Stop if label does not fit! */
			if ((cp2 - fsnum) + 2 > (cp1 - fsnums)) break;

			while (cp2 > fsnum) {
				*--cp1 = *--cp2;
			}
			*--cp1 = ' ';
			*--cp1 = ',';

			plist [nplot++] = i;
			CLRBIT (left, i);
			--nleft;

			fsp = cip -> full_trees [i];
			fst_comment (fsp);
			draw_fst (fsp, cip);

			pts = fsp -> terminals;
			for (j = 0; j < pts -> n; j++) {
				SETBIT (tmask, pts -> a [j].pnum);
			}
		}

		(void) printf ("\tPlot_Terminals\n");
		(void) sprintf (title,
				"FST%s %s.",
				(nplot > 1) ? "s" : "",
				cp1 + 2);
		end_plot (title);
	}

	page_break ();

	free ((char *) plist);
	free ((char *) tmask);
	free ((char *) left);
}

/*
 * This routine generates some Postscript commands to plot a SUBSET of
 * the given full-sets in overlaid fashion.
 */

	void
overlay_plot_subset (

char *			title,		/* IN - title to display with. */
bitmap_t *		fset_mask,	/* IN - subset of full-sets to plot */
struct cinfo *		cip,		/* IN - compatibility info */
enum plot_size		plot_size	/* IN - size of plot to produce */
)
{
int			i;
int			n;
int *			vp1;
int *			vp2;
struct full_set *	fsp;
char			buf1 [32];

	n = cip -> num_edges;

	if ((cip -> pts NE NULL) AND (cip -> full_trees NE NULL)) {
		/* Draw the FSTs with postscript. */
		begin_plot (plot_size);
		(void) printf ("\tPlot_Terminals\n");

		for (i = 0; i < n; i++) {
			if (NOT BITON (fset_mask, i)) continue;
			fsp = cip -> full_trees [i];
			fst_comment (fsp);
			draw_fst (fsp, cip);
		}

		end_plot (title);
	}
	else {
		/* Just print the hyperedges */
		for (i = 0; i < n; i++) {
			if (NOT BITON (fset_mask, i)) continue;
			printf ("Edge %d: ", i);
			vp1 = cip -> edge [i];
			vp2 = cip -> edge [i + 1];
			while (vp1 < vp2) {
				printf ("%d ", *vp1++);
			}
			dist_to_string (buf1,
					cip -> cost [i],
					&(cip -> scale));
			printf ("%s\n", buf1);
		}
	}
}

/*
 * This routine draws a single full Steiner tree.
 */

	static
	void
draw_fst (

struct full_set *	fsp,		/* IN - FST to draw */
struct cinfo *		cip		/* IN - compatibility info */
)
{
	switch (cip -> metric) {
	case RECTILINEAR:
		draw_rfst (fsp, cip);
		break;

	case EUCLIDEAN:
		draw_efst (fsp, cip);
		break;

	default:
		fatal ("draw_fst: Bug 1.");
		break;
	}
}

/*
 * This routine draws a single rectilinear FST.
 *
 * NOTE:  We ASSUME that the given FST graph represents a left-most Hwang
 * topology!  (Beyond this, we don't really care about the ordering of
 * terminals, edges or Steiner points.)  If we therefore draw all
 * "diagonal" (i.e., neither vertical nor horizontal) edges in left-most
 * fashion (vertical segment to left of the horizontal segment), then the
 * FST will be correctly drawn as a left-most Hwang topology.
 */

	static
	void
draw_rfst (

struct full_set *	fsp,		/* IN - full set to draw */
struct cinfo *		cip		/* IN - compatibility info */
)
{
int			i;
int			nt;
struct point *		p1;
struct point *		p2;
struct pset *		terms;
struct pset *		steins;
struct edge *		ep;
char			cbufx [32];
char			cbufy [32];
char			buf1 [256];
char			buf2 [256];

	terms = fsp -> terminals;
	steins = fsp -> steiners;

	nt = terms -> n;

	ep = fsp -> edges;
	for (i = 0; i < fsp -> nedges; i++, ep++) {
		if (ep -> p1 < nt) {
			p1 = &(terms -> a [ep -> p1]);
			(void) sprintf (buf1, "%d T", p1 -> pnum);
		}
		else {
			p1 = &(steins -> a [ep -> p1 - nt]);
			coord_to_string (cbufx, p1 -> x, &(cip -> scale));
			coord_to_string (cbufy, p1 -> y, &(cip -> scale));
			(void) sprintf (buf1, "%s\t%s", cbufx, cbufy);
		}

		if (ep -> p2 < nt) {
			p2 = &(terms -> a [ep -> p2]);
			(void) sprintf (buf2, "%d T", p2 -> pnum);
		}
		else {
			p2 = &(steins -> a [ep -> p2 - nt]);
			coord_to_string (cbufx, p2 -> x, &(cip -> scale));
			coord_to_string (cbufy, p2 -> y, &(cip -> scale));
			(void) sprintf (buf2, "%s\t%s", cbufx, cbufy);
		}

		if ((p1 -> x EQ p2 -> x) OR (p1 -> y EQ p2 -> y)) {
			(void) printf ("\t%s\t%s\tS\n",
				       buf1,
				       buf2);
		}
		else if (p1 -> x < p2 -> x) {
			(void) printf ("\t%s\t%s\tC\n",
				       buf1,
				       buf2);
		}
		else {
			(void) printf ("\t%s\t%s\tC\n",
				       buf2,
				       buf1);
		}
	}
}

/*
 * This routine draws a single Euclidean FST.
 */

	static
	void
draw_efst (

struct full_set *	fsp,		/* IN - FST to draw */
struct cinfo *		cip		/* IN - compatibility info */
)
{
int			i;
int			j;
int			nt;
struct point *		p1;
struct pset *		terms;
struct pset *		steins;
struct edge *		ep;
char			buf1 [32];
char			buf2 [32];

	terms = fsp -> terminals;
	steins = fsp -> steiners;

	nt = terms -> n;

	ep = fsp -> edges;
	for (i = 0; i < fsp -> nedges; i++, ep++) {
		j = ep -> p1;
		if (j < nt) {
			p1 = &(terms -> a [j]);
			(void) printf ("\t%d T", p1 -> pnum);
		}
		else {
			p1 = &(steins -> a [j - nt]);
			coord_to_string (buf1, p1 -> x, &(cip -> scale));
			coord_to_string (buf2, p1 -> y, &(cip -> scale));
			(void) printf ("\t%s\t%s", buf1, buf2);
		}
		j = ep -> p2;
		if (j < nt) {
			p1 = &(terms -> a [j]);
			(void) printf ("\t%d T\tS\n", p1 -> pnum);
		}
		else {
			p1 = &(steins -> a [j - nt]);
			coord_to_string (buf1, p1 -> x, &(cip -> scale));
			coord_to_string (buf2, p1 -> y, &(cip -> scale));
			(void) printf ("\t%s\t%s\tS\n", buf1, buf2);
		}
	}
}

/*
 * This routine plots an LP solution.  This is a set of full sets in which
 * full set i has weight Wi, where 0 <= Wi <= 1.  The full sets with
 * weight of 1 are drawn normally.  The fractional ones are drawn as
 * gray-scale "stars" emanating from the center of mass of the terminals.
 */

	void
plot_lp_solution (

char *		title,		/* IN - title for plot. */
double *	weights,	/* IN - weight of each full set. */
struct cinfo *	cip,		/* IN - compatibility info. */
enum plot_size	plot_size	/* IN - size of plot to produce. */
)
{
int			i;
int			n;
struct full_set *	fsp;
int *			vp1;
int *			vp2;

	n = cip -> num_edges;

	if ((cip -> pts NE NULL) AND (cip -> full_trees NE NULL)) {
		/* Draw the FSTs with postscript. */
		begin_plot (plot_size);

		for (i = 0; i < n; i++) {
			if (weights [i] <= 0.000001) continue;
			fsp = cip -> full_trees [i];
			draw_fractional_fst (fsp, weights [i], cip);
		}

		(void) printf ("\tPlot_Terminals\n");

		end_plot (title);
	}
	else {
		/* Just output the weighted hyperedges. */
		printf ("\n");
		for (i = 0; i < n; i++) {
			if (weights [i] <= 0.000001) continue;
			printf ("x^%d = %10.8g\t", i, weights [i]);
			vp1 = cip -> edge [i];
			vp2 = cip -> edge [i + 1];
			while (vp1 < vp2) {
				printf (" %d", *vp1++);
			}
			printf ("\n");
		}
	}
}

/*
 * This routine plots a particular subtour violation S within an LP solution.
 * Only FSTs having at least 2 vertices in the subtour S are displayed.
 * The full sets with weight of 1 are drawn normally.  The fractional ones
 * are drawn as gray-scale "stars" emanating from the center of mass of the
 * terminals.
 */

	void
plot_subtour (

char *		title,		/* IN - title for plot. */
double *	weights,	/* IN - weight of each full set. */
struct cinfo *	cip,		/* IN - compatibility info. */
bitmap_t *	S,		/* IN - subtour to plot. */
enum plot_size	plot_size	/* IN - size of plot to produce. */
)
{
int			i;
int			j;
int			k;
int			n;
struct full_set *	fsp;
int *			vp1;
int *			vp2;

	n = cip -> num_edges;

	if ((cip -> pts NE NULL) AND (cip -> full_trees NE NULL)) {
		/* Draw the FSTs with postscript. */
		begin_plot (plot_size);

		for (i = 0; i < n; i++) {
			if (weights [i] <= 0.000001) continue;
			k = 0;
			vp1 = cip -> edge [i];
			vp2 = cip -> edge [i + 1];
			while (vp1 < vp2) {
				j = *vp1++;
				if (BITON (S, j)) {
					++k;
				}
			}
			if (k < 2) continue;
			fsp = cip -> full_trees [i];
			draw_fractional_fst (fsp, weights [i], cip);
		}

		(void) printf ("\t0.75 setgray\n");
		(void) printf ("\tPlot_Terminals\n");
		(void) printf ("\t0 setgray\n");

		for (j = 0; j < cip -> num_verts; j++) {
			if (NOT BITON (S, j)) continue;
			printf ("\t%d\tPT\n", j);
		}

		end_plot (title);
	}
	else {
		/* Just output the weighted hyperedges. */
		printf ("\n");
		for (i = 0; i < n; i++) {
			if (weights [i] <= 0.000001) continue;
			printf ("x^%d = %10.8g\t", i, weights [i]);
			vp1 = cip -> edge [i];
			vp2 = cip -> edge [i + 1];
			while (vp1 < vp2) {
				printf (" %d", *vp1++);
			}
			printf ("\n");
		}
	}
}

/*
 * This routine draws a single fractional-weight full set.  We draw these
 * as a "star" using the center-of-mass of the terminals as the hub.  The
 * weight is used to determine the darkness of the lines.
 */

	static
	void
draw_fractional_fst (

struct full_set *	fsp,		/* IN - full set to draw */
double			weight,		/* IN - weight for full set */
struct cinfo *		cip		/* IN - compatibility info */
)
{
int			i;
int			n;
coord_t			cx;
coord_t			cy;
struct pset *		terms;
struct point *		p1;
char			buf1 [32];
char			buf2 [32];

	terms = fsp -> terminals;

	if (weight + 0.000001 >= 1.0) {
		/* Draw integral full sets "normally"... */
		fst_comment (fsp);
		(void) printf ("\t0 setgray\n");
		draw_fst (fsp, cip);
		return;
	}

	fst_comment (fsp);

	n = terms -> n;

	/* Determine the coordinates of the "hub". */
	p1 = &(terms -> a [0]);
	cx = 0.0;
	cy = 0.0;
	for (i = 0; i < n; p1++, i++) {
		cx += p1 -> x;
		cy += p1 -> y;
	}
	cx /= n;
	cy /= n;
	coord_to_string (buf1, cx, &(cip -> scale));
	coord_to_string (buf2, cy, &(cip -> scale));

	(void) printf ("\t%f setgray\n", 1.0 - weight);

	p1 = &(terms -> a [0]);
	for (i = 0; i < n; p1++, i++) {
		(void) printf ("\t%d T\t%s\t%s\tS\n",
			       p1 -> pnum, buf1, buf2);
	}
}

/*
 * This routine emits a Postscript comment describing the FST.
 */

	static
	void
fst_comment (

struct full_set *	fsp		/* IN - full set to describe */
)
{
int			i;
int			n;
struct pset *		terms;
struct point *		p1;

	terms = fsp -> terminals;

	(void) printf (" %% fs%d:", fsp -> tree_num);
	n = terms -> n;
	p1 = &(terms -> a [0]);
	for (i = 0; i < n; p1++, i++) {
		(void) printf (" %lu", (int32u) (p1 -> pnum));
	}
	(void) printf ("\n");
}

/*
 * This routine draws a line segment between two points.
 */

	void
draw_segment (

struct point *		p1,	/* IN - first point */
struct point *		p2,	/* IN - second point */
struct scale_info *	sip	/* IN - problem scaling info */
)
{
char		buf1 [32];
char		buf2 [32];
char		buf3 [32];
char		buf4 [32];

	coord_to_string (buf1, p1 -> x, sip);
	coord_to_string (buf2, p1 -> y, sip);
	coord_to_string (buf3, p2 -> x, sip);
	coord_to_string (buf4, p2 -> y, sip);

	(void) printf ("	%s	%s	%s	%s	S\n",
		       buf1, buf2, buf3, buf4);
}

/*
 * This routine emits appropriate Postscript code to begin a plot of
 * the given size.  If this is the first plot on a page, then we also
 * emit DSC comments for the page number.  This helps out ghostview and
 * other Postscript previewers.
 */

	void
begin_plot (

enum plot_size		size		/* IN - size of plot to begin */
)
{
	current_plot_size = size;
	switch (size) {
	case BIG_PLOT:
		page_break ();
		announce_new_page ();
		(void) printf ("BeginPlot\n");
		break;

	case SMALL_PLOT:
		if (small_plots_on_page EQ 0) {
			announce_new_page ();
		}
		(void) printf ("BeginSmallPlot\n");
		break;

	default:
		fatal ("begin_plot: Bug 1.");
		break;
	}
}

/*
 * This routine emits appropriate Postscript code to end the plot that
 * is currently in progress.  We also track the number of finished
 * plots per page here.
 */

	void
end_plot (

char *			title		/* IN - title for plot */
)
{
	(void) printf ("  (%s)\n", title);
	switch (current_plot_size) {
	case BIG_PLOT:
		(void) printf ("EndPlot\n\n");
		break;

	case SMALL_PLOT:
		(void) printf ("EndSmallPlot2\n\n");
		++small_plots_on_page;
		if (small_plots_on_page >= SMALL_PLOTS_PER_PAGE) {
			small_plots_on_page = 0;
		}
		break;

	default:
		fatal ("end_plot: Bug 1.");
		break;
	}
}

/*
 * This routine puts a %%Page: comment into the output to mark a page
 * boundary.  This is for the benefit of Ghostview and other Postscript
 * previewers.
 */

	static
	void
announce_new_page (void)

{
	++current_page;
	(void) printf ("%%%%Page: %lu %lu\n",
		       (int32u) current_page,
		       (int32u) current_page);
}

/*
 * This routine introduces a page break.  This is needed when doing
 * small plots, and the page has not yet filled up.
 */

	static
	void
page_break (void)

{
	if (small_plots_on_page NE 0) {
		(void) printf ("FlushSmallPlot\n");
		small_plots_on_page = 0;
	}
}