Re: XForms: Printing of xyplots?

Steve Lamont (spl@szechuan.ucsd.edu)
Thu, 14 Aug 97 06:28:38 PDT

To subscribers of the xforms list from spl@szechuan.ucsd.edu (Steve Lamont) :

> As far as I am concerned, this is exactly what I was hoping for! I am
> using Xforms for a application controlling a mass spec. Within the
> application the aquired data are plotted in an xyplot object and
> should be optionally printed as well.

Here's a hack that I use to do printing of PostScript graphs. It does
not directly interface to XForms (in fact, the code is devoid of any
XForms or Xlib calls whatever) but does give you a plot as a reward
for your efforts.

Some known bugs or limitations:

It is US-centric -- it is designed to print on US standard 8.5 x 11
inch paper. I don't think it would be too difficult to make it print
on some other format, though.

The positioning of the labels is fixed.

It doesn't handle overlays.

There are probably others which I've forgotten.

To use, include the file spl_ps.h so you get the prototypes and the
typedefs. It would be (schematically) used as follows:

f = popen( "lpr", "w" );

postscript_header( f );

postscript_draw_graph( f, ..., GraphTypeCurve );

pclose( f );

It is extracted from a larger library of throughly undocumented hacks
and utilities which I've crufted together over the last three or for
projects using XForms.

spl

- - - begin spl_ps.h - - -
/*
* I hereby place this stuff in the public domain. Do with it what you
* will but don't come to me if all your parrots suddenly turn killer,
* you cat is missing for weeks and comes home reeking of catnip and
* gin, or all your programs suddenly start core dumping simply
* because you read this file -- it ain't my fault. Use at your own risk.
*
* Distributed for educational and amusement purposes only. Do not
* spray into eyes or look into the laser beam. Post no bills. Close
* cover before striking. Objects are closer than they seem. Keep out
* of the reach of children or Computer Science professors. Induce
* vomiting. This is not a toy. If condition persists, see a physician
* or licensed medical practitioner. May cause drowsiness.
*/

typedef enum {
GraphTypeInvalid,
GraphTypePoint,
GraphTypeCurve,
GraphTypePointCurve,
GraphTypeHistogram,
GraphTypeLastOne
} GraphTypeE;

void postscript_text_left( FILE *f, int x, int y, char *fmt, ... );
void postscript_text_center( FILE *f, int x, int y, char *fmt, ... );
void postscript_text_right( FILE *f, int x, int y, char *fmt, ... );
void postscript_header( FILE *f );
void make_nice_ticks(double min, double max, int n_ticks, int *ticks,
float *graph_min, float *graph_max );
void postscript_draw_graph( FILE *f,
char *x_label, int x_org, int width,
char *y_label, int y_org, int height,
double x_min, double x_max, int x_ticks,
double y_min, double y_max, int y_ticks,
float *x, float *y, int n,
GraphTypeE graph_type );

- - - end spl_ps.h - - -

- - - begin spl_ps.c - - -
#include <stdio.h>
#include <math.h>

#include "spl_ps.h"

/*
* I hereby place this stuff in the public domain. Do with it what you
* will but don't come to me if all your parrots suddenly turn killer,
* you cat is missing for weeks and comes home reeking of catnip and
* gin, or all your programs suddenly start core dumping simply
* because you read this file -- it ain't my fault. Use at your own risk.
*
* Distributed for educational and amusement purposes only. Do not
* spray into eyes or look into the laser beam. Post no bills. Close
* cover before striking. Objects are closer than they seem. Keep out
* of the reach of children or Computer Science professors. Induce
* vomiting. This is not a toy. If condition persists, see a physician
* or licensed medical practitioner. May cause drowsiness.
*/

static void draw_ticks(FILE *f, int x0, int x1, int y0, int y1,
int ticks, float min, float max);

void postscript_draw_graph( FILE *f,
char *x_label, int x_org, int width,
char *y_label, int y_org, int height,
double x_min, double x_max, int x_ticks,
double y_min, double y_max, int y_ticks,
float *x, float *y, int n,
GraphTypeE graph_type )

{

float graph_x_min;
float graph_y_min;
float graph_x_max;
float graph_y_max;
int i;
float m_x;
float m_y;

fprintf( f, "gsave\n" );
fprintf( f, "%d %d translate\n", x_org, y_org );

postscript_text_right( f, width, 0,
"\\s15\\(HB%s", x_label );
postscript_text_left( f, 0, height - 15,
"\\s15\\(HB%s", y_label );

fprintf( f, "newpath\n" );
width -= 72;
height -= 72;
fprintf( f, "72 36 translate\n" );
fprintf( f, "%d %d moveto\n", 0, height );
fprintf( f, "%d %d lineto\n", 0, 0 );
fprintf( f, "%d %d lineto\n", width, 0 );
fprintf( f, "stroke\n" );

if ( !x_ticks )
make_nice_ticks( x_min, x_max, 5,
&x_ticks, &graph_x_min, &graph_x_max );
else {

graph_x_min = x_min;
graph_x_max = x_max;

}
draw_ticks( f, 0, width, 0, 0, x_ticks, graph_x_min, graph_x_max );

if ( !y_ticks )
make_nice_ticks( y_min, y_max, 5,
&y_ticks, &graph_y_min, &graph_y_max );
else {

graph_y_min = y_min;
graph_y_max = y_max;

}
draw_ticks( f, 0, 0, 0, height, y_ticks, graph_y_min, graph_y_max );

fprintf( f, "gsave\n" );
fprintf( f, "%g %g scale\n",
m_x = width / ( graph_x_max - graph_x_min ),
m_y = height / ( graph_y_max - graph_y_min ) );
fprintf( f, "%g setlinewidth\n", 1.0 / ( ( m_x < m_y ) ? m_y : m_x ) );
fprintf( f, "%f %f translate\n", -graph_x_min, -graph_y_min );
switch ( graph_type ) {

case GraphTypePoint: {

for ( i = 0; i < n; i++ ) {

fprintf( f, "newpath\n" );

fprintf( f, "%g %g moveto\n",
x[i] - ( 2.0 / m_x ),
y[i] - ( 2.0 / m_y ) );
fprintf( f, "%g %g lineto\n",
x[i] - ( 2.0 / m_x ),
y[i] + ( 2.0 / m_y ) );
fprintf( f, "%g %g lineto\n",
x[i] + ( 2.0 / m_x ),
y[i] + ( 2.0 / m_y ) );
fprintf( f, "%g %g lineto\n",
x[i] + ( 2.0 / m_x ),
y[i] - ( 2.0 / m_y ) );
fprintf( f, "%g %g lineto\n",
x[i] - ( 2.0 / m_x ),
y[i] - ( 2.0 / m_y ) );

fprintf( f, "strokepath\n" );
fprintf( f, "fill\n" );

}

break;

}
case GraphTypeCurve: {

fprintf( f, "newpath\n" );
fprintf( f, "%g %g moveto\n", x[0], y[0] );
for ( i = 1; i < n; i++ )
fprintf( f, "%g %g lineto\n", x[i], y[i] );
fprintf( f, "stroke\n" );
break;

}
case GraphTypePointCurve: {

fprintf( f, "newpath\n" );
fprintf( f, "%g %g moveto\n", x[0], y[0] );
for ( i = 1; i < n; i++ )
fprintf( f, "%g %g lineto\n", x[i], y[i] );
fprintf( f, "stroke\n" );

for ( i = 0; i < n; i++ ) {

fprintf( f, "newpath\n" );

fprintf( f, "%g %g moveto\n",
x[i] - ( 2.0 / m_x ),
y[i] - ( 2.0 / m_y ) );
fprintf( f, "%g %g lineto\n",
x[i] - ( 2.0 / m_x ),
y[i] + ( 2.0 / m_y ) );
fprintf( f, "%g %g lineto\n",
x[i] + ( 2.0 / m_x ),
y[i] + ( 2.0 / m_y ) );
fprintf( f, "%g %g lineto\n",
x[i] + ( 2.0 / m_x ),
y[i] - ( 2.0 / m_y ) );
fprintf( f, "%g %g lineto\n",
x[i] - ( 2.0 / m_x ),
y[i] - ( 2.0 / m_y ) );

fprintf( f, "strokepath\n" );
fprintf( f, "fill\n" );

}
break;

}
case GraphTypeHistogram: {

fprintf( f, "newpath\n" );
for ( i = 0; i < n; i++ ) {

fprintf( f, "%g %g moveto\n", x[i], graph_y_min );
fprintf( f, "%g %g lineto\n", x[i], y[i] );

}
fprintf( f, "stroke\n" );

break;

}
default: {

fprintf( stderr, "Bad graph type! (%d)", graph_type );
exit( 1 );

}

}
fprintf( f, "grestore\n" );
fprintf( f, "grestore\n" );

fprintf( f, "showpage\n" );

}

void postscript_text_left( FILE *f, int x, int y, char *fmt, ... )

{

va_list list;
char string[512];

va_start( list, fmt );
vsprintf( string, fmt, list );
va_end( list );

fprintf( f, "gsave\n" );
fprintf( f, "newpath\n" );
fprintf( f, "%d %d moveto\n", x, y );
parse_string( f, string, "show" );
fprintf( f, "grestore\n" );

}

void postscript_text_center( FILE *f, int x, int y, char *fmt, ... )

{

va_list list;
char string[512];

va_start( list, fmt );
vsprintf( string, fmt, list );
va_end( list );

fprintf( f, "gsave\n" );
fprintf( f, "newpath\n" );
fprintf( f, "%d %d moveto\n", x, y );
fprintf( f, "0\n" );
parse_string( f, string, "stringwidth pop add" );
fprintf( f, "-2 div\n" );
fprintf( f, "0 rmoveto\n" );
parse_string( f, string, "show" );
fprintf( f, "grestore\n" );

}

void postscript_text_right( FILE *f, int x, int y, char *fmt, ... )

{

va_list list;
char string[512];

va_start( list, fmt );
vsprintf( string, fmt, list );
va_end( list );

fprintf( f, "gsave\n" );
fprintf( f, "newpath\n" );
fprintf( f, "%d %d moveto\n", x, y );
fprintf( f, "0\n" );
parse_string( f, string, "stringwidth pop add" );
fprintf( f, "-1 mul\n" );
fprintf( f, "0 rmoveto\n" );
parse_string( f, string, "show" );
fprintf( f, "grestore\n" );

}

void postscript_header( FILE *f )

{

fprintf( f, "%%!PS-Adobe-3.0\n" );
fprintf( f, "%%%%Creator: XFido Version 2.0\n" );
fprintf( f, "%%%%DocumentNeededResources: font Times-Italic\n" );
fprintf( f, "%%%%+ font Times-Bold\n" );
fprintf( f, "%%%%+ font Times-BoldItalic\n" );
fprintf( f, "%%%%+ font Courier\n" );
fprintf( f, "%%%%+ font Courier-Bold\n" );
fprintf( f, "%%%%+ font Courier-Oblique\n" );
fprintf( f, "%%%%+ font Courier-BoldOblique\n" );
fprintf( f, "%%%%+ font Helvetica\n" );
fprintf( f, "%%%%+ font Helvetica-Bold\n" );
fprintf( f, "%%%%+ font Helvetica-Oblique\n" );
fprintf( f, "%%%%+ font Helvetica-BoldOblique\n" );
fprintf( f, "%%%%Pages: 1\n" );
fprintf( f, "%%%%PageOrder: Ascend\n" );
fprintf( f, "%%%%Orientation: Portrait\n" );
fprintf( f, "%%%%EndComments\n" );

}

void make_nice_ticks( double min, double max, int n_ticks,
int *ticks,
float *graph_min, float *graph_max )

{

double range = nicenum( max - min, 0 );
double d = nicenum( range / ( n_ticks - 1 ), 1 );

*graph_min = floor( min / d ) * d;
*graph_max = ceil( max / d ) * d;
*ticks = ( ( *graph_max + ( d * 0.5 ) ) - *graph_min ) / d;

}

static double nicenum( double x, int round )

{

double exponent;
double f;
double nf;

exponent = floor( log10( x ) );
f = x / pow( 10.0, exponent );
if ( round ) {

if ( f < 1.5 )
nf = 1.0;
else if ( f < 3.0 )
nf = 2.0;
else if ( f < 7.0 )
nf = 5.0;
else
nf = 10.0;

} else {

if ( f <= 1.0 )
nf = 1.0;
else if ( f <= 2.0 )
nf = 2.0;
else if ( f <= 5.0 )
nf = 5.0;
else
nf = 10.0;

}
return nf * pow( 10.0, exponent );

}

static void draw_ticks( FILE *f,
int x0, int x1, int y0, int y1,
int ticks, float min, float max )

{

float dx = x1 - x0;
float dy = y1 - y0;
float dt = max - min;
float m_x = dx / ticks;
float m_y = dy / ticks;
float m_t = dt / ticks;
float b_x = x0;
float b_y = y0;
float b_t = min;
int tick;

fprintf( f, "newpath\n" );
for ( tick = 0; tick <= ticks; tick++ ) {

fprintf( f, "%g %g moveto\n",
( tick * m_x ) + b_x,
( tick * m_y ) + b_y );
fprintf( f, "%g %g rlineto\n",
( !m_x ? -7.2 : 0.0 ),
( !m_y ? -7.2 : 0.0 ) );

if ( m_x )
postscript_text_center( f,
( int ) ( ( tick * m_x ) + b_x ),
-20,
"\\s15\\(HB%g", ( tick * m_t ) + b_t );
else
postscript_text_right( f,
-8,
( int ) ( ( tick * m_y ) + b_y - 6 ),
"\\s15\\(HB%g", ( tick * m_t ) + b_t );

}
fprintf( f, "stroke\n" );

}
- - - end spl_ps.c - - -
_________________________________________________
To unsubscribe, send the message "unsubscribe" to
xforms-request@bob.usuf2.usuhs.mil or see
http://bob.usuf2.usuhs.mil/mailserv/xforms.html
Xforms Home Page: http://bragg.phys.uwm.edu/xforms
List Archive: http://bob.usuf2.usuhs.mil/mailserv/list-archives/