Generic Graph Paper.

Below is a postscript file to draw simple graphs with some control over the axis labels, tick size and the like, as well as some routines to draw simple graphs. As set up it produces the postscript code for an encapsulated postscript file.

Just copy the data below the line and save it as a text file. It should print directly or it can be run through "Distiller" to get a PDF file which can be viewed via "Acrobat" or "Acrobat Reader". It can also be modified to produce more interesting graphs.

It has a built-in bias towards the origin but otherwise is pretty flexible with a minimal amount of work from the user. To graph $y=f(x)$ from $x=a$ to $x=b$ you need to write a postscript function that takes an $x$ value on the stack and replaces it on the stack with $f(x)$. Polynomials are especially easy. To graph $y=x^5-3x^4+x^2-1$ from $x=-1$ to $x=3$ needs the following two lines of postscript code:

/YeqFun{ [ 1 -3 0 1 0 -1 ] poly}def
-1 .1 3 graph stroke

The graph is made up of a collection of lines and the smaller the ".1" the closer the interpolated points are. Experiment to get a nice graph.

stroke is needed since graph does not automatically do strokes and so can be used to compute fills. Here is an example.

The size of the ticks is set with /tickSize{??}def and by altering it you cam produce a variable tick effect as in this example.

Polar coordinates are also supported. The function /polarcoordinates expects to find r and theta on the stack as input. They are popped and x and y are left on the stack. Hence "5 0 polar moveto" will move you to the point (5,0) in terms of xTick and yTick. There are three functions COS, SIN and TAN which expect radians as input rather than the postscript degrees. Finally the routines polar and Polar will draw polar graphs. They are parametrized polar graphs and need functions /ReqFun and /ThetaeqFun defined. By default, these are both the identity, so

/ReqFun{ 2 mul SIN 6.25 mul}def
0 .1 pi polar stroke

graphs the 4-leafed rose r=6.25 sin(2theta). Here is an example with some fills.

There are also routines to deal with functions having singularities. An example is here.

%!PS-Adobe-3.0 EPSF-3.0
%%Title: Blank Graph
%%Creator:Laurence R. Taylor  taylor.2@nd.edu www.nd.edu/~taylor
%%Distribute freely but modifications may remain private
%%CreationDate: ??
%%Version: 1.0.0
%%Data is set near the end of this file. (Search for data.)
%%EXCEPT that you may need to adjust the BoundingBox
%%BoundingBox: -100 -100 100 100

save
%%Initialize variables
/arrx 0 def
/varx 0 def
/ReqFun{}def
/ThetaeqFun{}def
/XeqFun{}def
/YeqFun{}def
/fun_v {}def
/fun_a {}def
/fun_b {}def
/start_v 0 def
/inc_v 0 def
/end_v 0 def
/mX 0 def
/pX 0 def
/mY 0 def
/pY 0 def
/xTick 0 def
/yTick 0 def
/tickSize{3}def
/sxn 0 def

%%TEX fonts
/fontscale{8}def
/vfont{fontscale}def %% vertical distance from bottom of tick to baseline of labels
/hfont{fontscale 2 div}def %% horizontal distance from tick to right edge of labels
/CMR{
/CMR10 findfont fontscale scalefont setfont
}def

/CMMI{
/CMMI10 findfont fontscale scalefont setfont
}def

/poly{
%% Computes a polynomial
%% /YeqFun{ [a_n ... a_1 a_0] poly}def
%% If followed by graph graphs the polynomial
%% y=a_nx^n+ ... +a_1x+a_0
/arrx exch def
/varx exch def
0 0 1 arrx length 1 sub
{arrx exch get exch varx mul add}for
} def

%% pops the arguments and pushes function1(number)+function2(number)
/fun_a exch def
/fun_b exch def
}def

/MUL{
%% USAGE: number {function1}{function2} MUL
%% pops the arguments and pushes function1(number)*function2(number)
/fun_a exch def
/fun_b exch def
dup fun_b exch fun_a mul
}def

/DIV{
%% USAGE: number {function1}{function2} DIV
%% pops the arguments and pushes function1(number)/function2(number)
%% after checking function2(number) ne 0
/fun_a exch def
/fun_b exch def
dup fun_b exch fun_a
dup 0 eq{pop pop pX mX sub pY mY sub add 100 add}{div}ifelse
}def

/pi{3.1415}bind def
%% - postscript trig functions take degree arguments

%% Any plotting function can have a sing_ prepended
%% This plots just as the non-sing version except that
%% each point is checked to see how far it is from the
%% previous point and if it is too far, we just moveto
%% the point rather than lineto it. This prevents lines
%% connecting jump discontinuities of the function.
%% (VERY steep parts may also be skipped.)
%% The sing_ version is slower than the non-sing version
%% and should only be used when the regular version shows
%% lines you don't want.

/PLOT{
/fun_v exch def %% Pops the parameter value and
%% pushes the xy-coordinates of the point
/end_v exch def
/inc_v exch def
/start_v exch def
start_v fun_v moveto %% moveto start
start_v inc_v end_v {fun_v lineto} for %% connect the dots
}def

/sing_PLOT{
/fun_v exch def
/end_v exch def
/inc_v exch def
/start_v exch def
start_v fun_v /last_y exch def /last_x exch def %% initialize last_?
last_x last_y moveto                            %% and move to there to start
start_v inc_v end_v { %% initialize for-loop
fun_v /next_y exch def /next_x exch def %% get next_?
next_x next_y                           %% arguments for lineto/moveto
0 last_y next_y sub abs pY sub mY add le{moveto}{ %% dy too big? moveto
0 last_x next_x sub abs pX sub mX add le{moveto}{ %% dx too big? moveto
lineto}ifelse}ifelse %% otherwise, lineto
/last_x next_x def/last_y next_y def %% shift next_? to last_?
} for
}def

%% Cartesian coordinate stuff
/XY{
%% Pops a parameter value off the stack and pushes the xy-coordinates
%% of the point in the current parameterized curve
dup XeqFun xTick mul exch YeqFun yTick mul
}def

/graph{
%% Just like Graph but XeqFun and YeqFun are reset to {}
%% This usually means that one can graph
%% y=f(x) by just defining YeqFun OR
%% x=f(y) by just defining XeqFun
%% USAGE:  start_v increment_v end_v graph
Graph /XeqFun{}def /YeqFun{}def
}def

/Graph{
%% Takes the current definitions of XeqFun and YeqFun
%% and graphs the parameterized curve.
%% It is automatically scaled so that x=1 appears at the first
%% xTick and y=1 appears at the first yTick
%% USAGE:  start_v increment_v end_v Graph
%% Will graph y=YeqFun(t) x=XeqFun(t) from start_v to end_v by drawing lines
%% between successive points with t increased by increment_v.
%% Needs to be stroked to see graph so we can use graph
%% to make paths.
%% XeqFun and YeqFun are retained.
{XY} PLOT
}def

/sing_graph{sing_Graph /XeqFun{}def /YeqFun{}def
}def
/sing_Graph{{XY} sing_PLOT}def
%% POLAR COORDINATE STUFF

/polarcoordinates{
%% pops r and theta from the stack and pushes x and y
dup 3 -1 roll dup 4 1 roll exch COS mul xTick mul 3 1 roll SIN mul yTick mul
}def

/RT{
%% Pops a parameter value off the stack and pushes the r\theta-coordinates
%% of the point in the current parameterized polar curve
dup ReqFun exch ThetaeqFun
}def

/XYpolar{
%% Pops a parameter value off the stack and pushes the xy-coordinates
%% of the point in the current parameterized polar curve
RT polarcoordinates
}def

/polar {
%% Takes the current definitions of ReqFun and ThetaeqFun
%% and graphs the parameterized curve in polar coordinates.
%% USAGE:  start_v increment_v end_v polar
%% Just like Polar except /ReqFun and /ThetaeqFun
%% are reset to {}
Polar /ReqFun{}def /ThetaeqFun{}def
}def

/Polar {
%% Takes the current definitions of ReqFun and ThetaeqFun
%% and graphs the parameterized curve in polar coordinates.
%% Need to write functions /ReqFun and /ThetaeqFun
%% USAGE:  start_v increment_v end_v Polar
{XYpolar} PLOT
}def

/sing_polar {
sing_Polar /ReqFun{}def /ThetaeqFun{}def
}def
/sing_Polar {
{XYpolar} sing_PLOT
}def

/axes{
newpath
mX 0 moveto
pX 0 lineto
stroke
0 mY moveto
0 pY lineto
stroke } def

/Xtick{
%% puts ticks along the x-axis from the origin
%% with displacement xTick until pX to the right and mX to the left
xTick xTick pX
{
0 moveto
0   tickSize rmoveto
0   tickSize -2 mul rlineto
}for
/nT{xTick -1 mul}def
nT nT mX
{
0 moveto
0   tickSize rmoveto
0   tickSize -2 mul rlineto
}for
stroke} def

/Ytick{
%% puts ticks along the y-axis from the origin
%% with displacement yTick until pY up and mY down
yTick yTick pY
{
0 exch moveto
tickSize  0 rmoveto
tickSize -2 mul  0 rlineto
}for
/nT{yTick -1 mul}def
nT nT mY
{
0 exch moveto
tickSize  0 rmoveto
tickSize -2 mul  0 rlineto
}for stroke
} def

/Show{true charpath }def %% use instead of show when writing self-writing labels
/label{ /opx{show}def /myshow{true charpath}def /myfill{}def LaBel}def
/Label{ /opx{exec}def /myshow{exec}def /myfill{fill}def LaBel}def
/LaBel{
%%Sticks an array of labels along axes.
%%Four variants
%%USEAGE: array (+x) label
%%USEAGE: array (-x) label
%%USEAGE: array (+y) label
%%USEAGE: array (-y) label
%%Puts elements of array along positive(+)/negative(-) x/y axis
%%starting at the origin.
%%Elements of array are just strings to be printed in the current font.
%%Use Label if the array elements print themselves. This is useful
%%for example if different elements of your string need to be printed
%%in different fonts.
%%EG: [ {CMMI (\31)show CMR(/2)show} ] prints pi over 2
/sxn exch def
/arrx exch def
0 1 arrx length 1 sub {
dup arrx exch get 0 0 moveto myshow
pathbbox /ur_y exch def/ur_x exch def/ll_y exch def/ll_x exch def
newpath
ur_x ll_x sub /my_x exch def
ur_y ll_y sub /my_y exch def
(+x) sxn eq
{dup xTick mul xTick add 0 moveto
my_x -2 div -1 tickSize mul vfont sub rmoveto
arrx exch get opx}if
(-x) sxn eq
{dup xTick mul xTick add -1 mul 0 moveto
my_x -2 div -1 tickSize mul vfont sub rmoveto
arrx exch get opx}if
(+y) sxn eq
{dup yTick mul yTick add 0 exch moveto
0 hfont sub tickSize sub my_x sub my_y -2 div rmoveto
arrx exch get opx}if
(-y) sxn eq
{dup yTick mul yTick add -1 mul 0 exch moveto
0 hfont sub tickSize sub my_x sub my_y -2 div rmoveto
arrx exch get opx}if
myfill}for}def

%%%%%%%  data %%%%%%%%%
%%BoundingBox: -100 -100 100 100
%%BoundingBox: left-x lower-y right-x upper-y
%% COPY "BoundingBox" LINE TO TOP OF FILE
%%%%%%%  Axes - generic values, reset as needed
/tickSize{3}def
/pX{100}def       %% Positive x-axis length
/pY{100}def       %% Positive y-axis length
/mX{-100}def      %% Negative x-axis length
/mY{-100}def      %% Negative y-axis length
/xTick{20} def    %% tick length for x-axis
/yTick{20} def    %% tick length for y-axis

/Narr{ [(-1) (-2) (-3) (-4) ]} def  %% the negative labels
/Parr{ [(1) (2) (3) (4)]} def       %% the positive labels

%%Page: 1 1
CMR  %% Sets basic TEX font
.4 setlinewidth

%% Fills probably go here

axes
.1 setlinewidth

Xtick
Ytick

Parr (+x) label
Parr (+y) label
Narr (-x) label
Narr (-y) label

%% Graphs probably go here

showpage
restore