ngspice/xgraph/ps.c

621 lines
17 KiB
C
Raw Normal View History

2004-01-25 10:00:31 +01:00
/*
* Postscript output for xgraph
*
* Rick Spickelmier
* David Harrison
*/
#include "copyright.h"
#include <stdio.h>
#include "xgraph.h"
/*
* Basic scaling parameters
*/
#define VDPI 1200.0
#define LDIM 11.0
#define SDIM 8.5
#define MICRONS_PER_INCH 2.54E+04
#define POINTS_PER_INCH 72.0
#define INCHES_PER_POINT 1.0/72.0
/*
* Aesthetic parameters (inches)
*/
#define PS_BDR_PAD 0.075
#define PS_AXIS_PAD 0.1
#define PS_LEG_PAD 0.025
#define PS_TICK_LEN 0.125
#define BASE_DASH (1.0/48.0)
#define BASE_WIDTH (1.0/8.0)
#define PS_AXIS_WBASE 1
#define PS_ZERO_WBASE 4
#define PS_DATA_WBASE 7
#define PS_PIXEL 4
#define PS_DOT 12
#define PS_MARK 12
/*
* Other constants
*/
#define FONT_WIDTH_EST 0.55
#define PS_MAX_SEGS 1000
#define PS_NO_TSTYLE -1
#define PS_NO_DSTYLE -1
#define PS_NO_WIDTH -1
#define PS_NO_LSTYLE -1
#define PS_NO_COLOR -1
/*
* Working macros
*/
#define OUT (void) fprintf
#define PS(str) OUT(psFile, str)
#define PSU(str) OUT(ui->psFile, str)
#define IY(val) (ui->height_devs - val)
#define TEXTCOLOR 0
#define MAXCOLOR 8 /* Number of gray scales supported */
/*
* Globals
*/
static double PS_scale; /* devs/micron */
/*
* Externals and forwards
*/
static void psScale(), psFonts(), psMarks(), psText(), psSeg(), psDot(), psEnd();
/*
* Local structures
*/
struct userInfo {
FILE *psFile;
int currentTextStyle;
int currentDashStyle;
int currentWidth;
int currentLStyle;
int currentColor;
int baseWidth;
int height_devs;
char *title_family;
double title_size;
char *axis_family;
double axis_size;
int flags;
};
int
rd(dbl)
double dbl;
/* Short and sweet rounding function */
{
if (dbl < 0.0) {
return ((int) (dbl - 0.5));
}
else {
return ((int) (dbl + 0.5));
}
}
/*ARGSUSED*/
int
psInit(psFile, width, height, tf, ts, af, as, flags, outInfo, errmsg)
FILE *psFile; /* Output file */
int width,
height; /* In microns */
char *tf,
*af; /* Title and axis font */
double ts,
as; /* Title and axis size */
int flags; /* Predicate flags */
xgOut *outInfo; /* Returned device info */
char errmsg[ERRBUFSIZE]; /* Returned error message */
/*
* The basic coordinate system is points (roughly 1/72 inch).
* However, most laser printers can do much better than that.
* We invent a coordinate system based on VDPI dots per inch.
* This goes along the long side of the page. The long side
* of the page is LDIM inches in length, the short side
* SDIM inches in length. We we call this unit a `dev'.
* We map `width' and `height' into devs.
*/
{
struct userInfo *ui;
double font_size;
ui = (struct userInfo *) Malloc(sizeof(struct userInfo));
ui->psFile = psFile;
ui->currentTextStyle = PS_NO_TSTYLE;
ui->currentDashStyle = PS_NO_DSTYLE;
ui->currentWidth = PS_NO_WIDTH;
ui->currentLStyle = PS_NO_LSTYLE;
ui->currentColor = PS_NO_COLOR;
ui->title_family = tf;
ui->title_size = ts;
ui->axis_family = af;
ui->axis_size = as;
/* Roughly, one-eighth a point in devs */
ui->baseWidth = rd(VDPI / POINTS_PER_INCH * BASE_WIDTH);
ui->flags = flags;
PS_scale = VDPI / MICRONS_PER_INCH;
outInfo->dev_flags = 0;
outInfo->area_w = rd(((double) width) * PS_scale);
outInfo->area_h = rd(((double) height) * PS_scale);
ui->height_devs = outInfo->area_h;
outInfo->bdr_pad = rd(PS_BDR_PAD * VDPI);
outInfo->axis_pad = rd(PS_AXIS_PAD * VDPI);
outInfo->legend_pad = rd(PS_LEG_PAD * VDPI);
outInfo->tick_len = rd(PS_TICK_LEN * VDPI);
/* Font estimates */
font_size = as * INCHES_PER_POINT * VDPI;
outInfo->axis_height = rd(font_size);
outInfo->axis_width = rd(font_size * FONT_WIDTH_EST);
font_size = ts * INCHES_PER_POINT * VDPI;
outInfo->title_height = rd(font_size);
outInfo->title_width = rd(font_size * FONT_WIDTH_EST);
outInfo->max_segs = PS_MAX_SEGS;
outInfo->xg_text = psText;
outInfo->xg_seg = psSeg;
outInfo->xg_dot = psDot;
outInfo->xg_end = psEnd;
outInfo->user_state = (char *) ui;
/* Postscript file identification */
PS("%%!\n");
/* Definitions */
psScale(psFile, width, height, flags);
psFonts(psFile);
psMarks(psFile);
PS("%%\n%% Main body begins here\n%%\n");
return 1;
}
static void
psHeader(psFile, docu_flag)
FILE *psFile;
int docu_flag;
/*
* Prints out a standard greeting to the Postscript file.
*/
{
PS("%%%%EndComments\n");
PS("%%\n");
PS("%% Xgraph postscript output\n");
PS("%% Rick Spickelmier and David Harrison\n");
PS("%% University of California, Berkeley\n");
if (docu_flag) {
PS("%%\n");
PS("%% Output produced for inclusion in another document.\n");
PS("%% This file will not work properly if sent directly to a printer.\n");
}
PS("%%\n");
}
static void
psScale(psFile, width, height, flags)
FILE *psFile; /* Output stream */
int width; /* Output width */
int height; /* Output height */
int flags; /* Output options */
/*
* This routine figures out how transform the basic postscript
* transformation into one suitable for direct use by
* the drawing primitives. Two variables X-CENTER-PLOT
* and Y-CENTER-PLOT determine whether the plot is centered
* on the page. If `flags' has D_DOCU set, then the plot
* will not be rotated or centered and a bounding box will
* be displayed.
*/
{
double factor;
double pnt_width,
pnt_height;
if (flags & D_DOCU) {
OUT(psFile, "%%%%BoundingBox: %ld %ld %ld %ld\n",
0, 0,
(int) (((double) width) /
(MICRONS_PER_INCH * INCHES_PER_POINT) + 0.5),
(int) (((double) height) /
(MICRONS_PER_INCH * INCHES_PER_POINT) + 0.5)
);
psHeader(psFile, 1);
PS("%% Rotation and centering are turned off for inclusion in a document\n");
}
else {
psHeader(psFile, 0);
PS("%% Scaling information\n");
PS("%%\n");
PS("%% Change these if you would like to change the centering\n");
PS("%% of the plot in either dimension\n");
PS("/X-CENTER-PLOT 1 def\n");
PS("/Y-CENTER-PLOT 1 def\n");
PS("%%\n");
/*
* Determine page size
*/
PS("%% Page size computation\n");
PS("clippath pathbbox\n");
PS("/page-height exch def\n");
PS("/page-width exch def\n");
PS("pop pop\n");
/*
* First: rotation. If the width is greater than the short dimension,
* do the rotation.
*/
pnt_width = ((double) width) / MICRONS_PER_INCH * POINTS_PER_INCH;
pnt_height = ((double) height) / MICRONS_PER_INCH * POINTS_PER_INCH;
PS("%% Determine whether rotation is required\n");
OUT(psFile, "%lg page-width gt\n", pnt_width);
PS("{ %% Rotation required\n");
PS(" 90 rotate\n");
PS(" 0 page-width neg translate\n");
PS(" %% Handle centering\n");
PS(" Y-CENTER-PLOT 1 eq { %% Center in y\n");
OUT(psFile, " page-height %lg sub 2 div\n", pnt_width);
PS(" } { %% Don't center in y\n");
PS(" 0\n");
PS(" } ifelse\n");
PS(" X-CENTER-PLOT 1 eq { %% Center in x\n");
OUT(psFile, " page-width %lg sub 2 div\n", pnt_height);
PS(" } { %% Don't center in x\n");
PS(" 0\n");
PS(" } ifelse\n");
PS(" translate\n");
PS("} { %% No rotation - just handle centering\n");
PS(" X-CENTER-PLOT 1 eq { %% Center in x\n");
OUT(psFile, " page-width %lg sub 2 div\n", pnt_width);
PS(" } { %% Don't center in x\n");
PS(" 0\n");
PS(" } ifelse\n");
PS(" Y-CENTER-PLOT 1 eq { %% Center in y\n");
OUT(psFile, " page-height %lg sub 2 div\n", pnt_height);
PS(" } { %% Don't center in y\n");
PS(" 0\n");
PS(" } ifelse\n");
PS(" translate\n");
PS("} ifelse\n");
}
/*
* Now: scaling. We have points. We want devs.
*/
factor = POINTS_PER_INCH / VDPI;
PS("%% Set the scale\n");
OUT(psFile, "%lg %lg scale\n", factor, factor);
}
static void
psFonts(psFile)
FILE *psFile; /* Output stream */
/*
* Downloads code for drawing title and axis labels
*/
{
PS("%% Font Handling Functions\n");
PS("%%\n");
PS("%% Function giving y-offset to center of font\n");
PS("%% Assumes font is set and uses numbers to gauge center\n");
PS("%%\n");
PS("/choose-font %% stack: fontsize fontname => ---\n");
PS("{\n");
PS(" findfont \n");
PS(" exch scalefont \n");
PS(" setfont\n");
PS(" newpath\n");
PS(" 0 0 moveto (0) true charpath flattenpath pathbbox\n");
PS(" /top exch def pop\n");
PS(" /bottom exch def pop\n");
PS(" bottom top bottom top add 2 div\n");
PS(" /center-font-val exch def \n");
PS(" /upper-font-val exch def \n");
PS(" /lower-font-val exch def\n");
PS("} def\n");
PS("%%\n");
PS("%% Justfication offset routines\n");
PS("%%\n");
PS("/center-x-just %% stack: (string) x y => (string) newx y\n");
PS("{\n");
PS(" exch 2 index stringwidth pop 2 div sub exch\n");
PS("} def\n");
PS("%%\n");
PS("/left-x-just %% stack: (string) x y => (string) newx y\n");
PS("{ \n");
PS("} def\n");
PS("%%\n");
PS("/right-x-just %% stack: (string) x y => (string) newx y\n");
PS("{\n");
PS(" exch 2 index stringwidth pop sub exch\n");
PS("} def\n");
PS("%%\n");
PS("/center-y-just %% stack: (string) x y => (string) x newy\n");
PS("{\n");
PS(" center-font-val sub\n");
PS("} def\n");
PS("%%\n");
PS("/lower-y-just %% stack: (string) x y => (string) x newy\n");
PS("{\n");
PS(" lower-font-val sub\n");
PS("} def\n");
PS("%%\n");
PS("/upper-y-just %% stack: (string) x y => (string) x newy\n");
PS("{\n");
PS(" upper-font-val sub\n");
PS("} def\n");
PS("%%\n");
PS("%% Shows a string on the page subject to justification\n");
PS("%% \n");
PS("/just-string %% stack: (string) x y just => ---\n");
PS("{\n");
PS(" dup 0 eq { pop center-x-just center-y-just } if\n");
PS(" dup 1 eq { pop left-x-just center-y-just } if\n");
PS(" dup 2 eq { pop left-x-just upper-y-just } if\n");
PS(" dup 3 eq { pop center-x-just upper-y-just } if\n");
PS(" dup 4 eq { pop right-x-just upper-y-just } if\n");
PS(" dup 5 eq { pop right-x-just center-y-just } if\n");
PS(" dup 6 eq { pop right-x-just lower-y-just } if\n");
PS(" dup 7 eq { pop center-x-just lower-y-just } if\n");
PS(" dup 8 eq { pop left-x-just lower-y-just } if\n");
PS(" moveto show\n");
PS("} def\n");
PS("%%\n");
}
static void
psMarks(psFile)
FILE *psFile;
/*
* Writes out marker definitions
*/
{
PS("%% Marker definitions\n");
PS("/mark0 {/size exch def /y exch def /x exch def\n");
PS("newpath x size sub y size sub moveto\n");
PS("size size add 0 rlineto 0 size size add rlineto\n");
PS("0 size size add sub 0 rlineto closepath fill} def\n");
PS("/mark1 {/size exch def /y exch def /x exch def\n");
PS("newpath x size sub y size sub moveto\n");
PS("size size add 0 rlineto 0 size size add rlineto\n");
PS("0 size size add sub 0 rlineto closepath stroke} def\n");
PS("/mark2 {/size exch def /y exch def /x exch def\n");
PS("newpath x y moveto x y size 0 360 arc stroke} def\n");
PS("/mark3 {/size exch def /y exch def /x exch def\n");
PS("newpath x size sub y size sub moveto x size add y size add lineto\n");
PS("x size sub y size add moveto x size add y size sub lineto stroke} def\n");
PS("/mark4 {/size exch def /y exch def /x exch def\n");
PS("newpath x size sub y moveto x y size add lineto\n");
PS("x size add y lineto x y size sub lineto\n");
PS("closepath stroke} def\n");
PS("/mark5 {/size exch def /y exch def /x exch def\n");
PS("x y size mark1\n");
PS("newpath x size sub y moveto size size add 0 rlineto stroke} def\n");
PS("/mark6 {/size exch def /y exch def /x exch def\n");
PS("newpath x y moveto x y size 0 360 arc fill} def\n");
PS("/mark7 {/size exch def /y exch def /x exch def\n");
PS("newpath x y moveto x size sub y size sub lineto\n");
PS("x size add y size sub lineto closepath fill\n");
PS("newpath x y moveto x size add y size add lineto\n");
PS("x size sub y size add lineto closepath fill} def\n");
}
static void
psText(state, x, y, text, just, style)
char *state; /* Really (struct userInfo *) */
int x,
y; /* Text position (devs) */
char *text; /* Text itself */
int just; /* Justification */
int style; /* Style */
/*
* Draws text at the given location with the given justification
* and style.
*/
{
struct userInfo *ui = (struct userInfo *) state;
if (TEXTCOLOR != ui->currentColor) {
OUT(ui->psFile, "%lg setgray\n", (double) TEXTCOLOR / 8);
ui->currentColor = TEXTCOLOR;
}
if (style != ui->currentTextStyle) {
switch (style) {
case T_AXIS:
OUT(ui->psFile, "%lg /%s choose-font\n",
ui->axis_size * INCHES_PER_POINT * VDPI, ui->axis_family);
break;
case T_TITLE:
OUT(ui->psFile, "%lg /%s choose-font\n",
ui->title_size * INCHES_PER_POINT * VDPI, ui->title_family);
break;
}
ui->currentTextStyle = style;
}
OUT(ui->psFile, "(%s) %d %d %d just-string\n", text, x, IY(y), just);
}
/*ARGSUSED*/
static void
psSeg(state, ns, seglist, width, style, lappr, color)
char *state; /* Really (struct userInfo *) */
int ns; /* Number of segments */
XSegment *seglist; /* X array of segments */
int width; /* Width of lines (devcoords) */
int style; /* L_AXIS, L_ZERO, L_VAR */
int lappr; /* Zero to seven */
int color; /* Zero to seven */
/*
* Draws a number of line segments. Grid lines are drawn using
* light lines. Variable lines (L_VAR) are drawn wider. This
* version ignores the color argument.
*/
{
struct userInfo *ui = (struct userInfo *) state;
int newwidth = 0,
i;
if ((style != ui->currentLStyle) || (width != ui->currentWidth)) {
switch (style) {
case L_AXIS:
newwidth = PS_AXIS_WBASE * ui->baseWidth;
PSU("[] 0 setdash\n");
break;
case L_ZERO:
newwidth = PS_ZERO_WBASE * ui->baseWidth;
PSU("[] 0 setdash\n");
break;
case L_VAR:
newwidth = PS_DATA_WBASE * ui->baseWidth;
break;
}
ui->currentWidth = MAX(newwidth, width);
ui->currentLStyle = style;
OUT(ui->psFile, "%d setlinewidth\n", ui->currentWidth);
}
if (width > 4) {
if (color > MAXCOLOR)
color -= MAXCOLOR;
else
lappr = 0;
}
else
color = TEXTCOLOR;
if ((lappr != ui->currentDashStyle) && (style == L_VAR)) {
if (lappr == 0) {
PSU("[] 0 setdash\n");
}
else {
OUT(ui->psFile, "[%lg] 0 setdash\n",
((double) lappr) * BASE_DASH * VDPI);
}
ui->currentDashStyle = lappr;
}
if ((color != ui->currentColor) && (style == L_VAR)) {
OUT(ui->psFile, "%lg setgray\n", (double) color / MAXCOLOR);
ui->currentColor = color;
}
PSU("newpath\n");
OUT(ui->psFile, " %d %d moveto\n", seglist[0].x1, IY(seglist[0].y1));
OUT(ui->psFile, " %d %d lineto\n", seglist[0].x2, IY(seglist[0].y2));
for (i = 1; i < ns; i++) {
if ((seglist[i].x1 != seglist[i - 1].x2) ||
(seglist[i].y1 != seglist[i - 1].y2)) {
OUT(ui->psFile, " %d %d moveto\n", seglist[i].x1, IY(seglist[i].y1));
}
OUT(ui->psFile, " %d %d lineto\n", seglist[i].x2, IY(seglist[i].y2));
}
PSU("stroke\n");
}
/*ARGSUSED*/
static void
psDot(state, x, y, style, type, color)
char *state; /* state information */
int x,
y; /* coord of dot */
int style; /* type of dot */
int type; /* dot style variation */
int color; /* color of dot */
/*
* Prints out a dot at the given location
*/
{
struct userInfo *ui = (struct userInfo *) state;
if (ui->currentDashStyle != PS_NO_DSTYLE) {
OUT(ui->psFile, "[] 0 setdash ");
ui->currentDashStyle = PS_NO_DSTYLE;
}
if (ui->currentWidth != PS_ZERO_WBASE * ui->baseWidth) {
ui->currentWidth = PS_ZERO_WBASE * ui->baseWidth;
OUT(ui->psFile, "%d setlinewidth ", ui->currentWidth);
}
if (color > MAXCOLOR)
color -= MAXCOLOR;
if ((color != ui->currentColor)) {
OUT(ui->psFile, "%lg setgray\n", (double) color / MAXCOLOR);
ui->currentColor = color;
}
switch (style) {
case P_PIXEL:
OUT(ui->psFile, "newpath %d %d moveto %d %d %d 0 360 arc fill\n",
x, IY(y), x, IY(y), PS_PIXEL * ui->baseWidth);
break;
case P_DOT:
OUT(ui->psFile, "newpath %d %d moveto %d %d %d 0 360 arc fill\n",
x, IY(y), x, IY(y), PS_DOT * ui->baseWidth);
break;
case P_MARK:
OUT(ui->psFile, "%d %d %d mark%d\n",
x, IY(y), PS_MARK * ui->baseWidth, type);
break;
}
return;
}
static void
psEnd(userState)
char *userState; /* state information */
{
struct userInfo *ui = (struct userInfo *) userState;
if (!(ui->flags & D_DOCU)) {
PSU("showpage\n");
}
PSU("%% End of xgraph output\n");
}