Q:

Simple 2D graph drawing - PostScript

Can you tell me an easy way to draw graph(2+x, sin(x), cos(x+3)/3.....) in PS format?
For example, I want to draw f(x) = 2+x, with the following values:
Table of values: 
Value of X = -5 | -4 | -3 | -2 | -1 | -0 | 1 .....
Value of Y = -3 | -2 | -1 |  0 |  1 |  2 | 3 .....

How to draw this graph? Draw lineto, draw polygon or use curve command?
What do you think is the best solution?

A:

There are a number of different ways you can do this. If you have a set of coordinates to plot, you can put them in an array, and draw the points while iterating through the array.
/XValues [ -5 -4 -3 -2 -1 0 1 ] def  % eg. XValues 0 get ==> -5
/YValues [ -3 -2 -1  0  1 2 3 ] def  %     YValues 0 get ==> -3

XValues 0 get YValues 0 get % X[0] Y[0]
moveto                      % move to first point
1 1 XValues length 1 sub {  % i    push integer i = 1 .. length(XValues)-1 on each iteration
    XValues                 % i XVal    push X array
    1 index                 % i XVal i  copy i from stack
    get                     % i x       get ith X value from array
    YValues                 % i x YVal
    2 index                 % i x YVal i  i is 1 position deeper now, so 2 index instead of 1
    get                     % i x y
    lineto                  % i    line to next point
    pop                     %      discard index variable
} for

Now, of course in Postscript the origin is at the bottom left corner by default, and 72 points make an inch. So these values (-5, -4, -2, etc) are not even going to be visible. So you usually want to start by translating to the center of where you want to draw the graph.
/Center { 300 400 } def  % roughly the middle of US letter-size paper
Center translate

Then you want to scale the coordinate system so the graph features are visible. Scalefactor = DesiredSize / ExistingSize.
You could scan the dataset to find the existing size.
/Xmin 1000000 def  % really high
/Xmax -1000000 def % really low
XValues {                   % x    forall pushes each X value
    dup Xmin lt {           % x    lower than Xmin?
        dup /Xmin exch def  % x    set Xmin
    } if                    % x
    dup Xmax gt {           % x    higher than Xmax?
        /Xmax exch def      %      set Xmax
    }{                      % x    else (lower than Xmax)
        pop                 %      discard X value
    } ifelse
} forall
/Datasize Xmax Xmin sub def   % X size is (Xmax-Xmin)

6 72 mul DataSize div  % scalefactor                6*72/(Xmax-Xmin)
dup                    % scalefactor scalefactor    use same scaling for x and y
scale

But there's a snag when you're doing line-drawing. The width of the lines you draw also depend upon the current coordinate space, so if you scale up the space by a large factor, your lines will become undesirably wide. You can either unscale back to the normal space after you describe the path but before you call stroke. Or, fix the linewidth at the same time you scale.
Since we know how much we've increased the line width (it's the same scaling factor), we can adjust the linewidth graphics parameter in the inverse direction.
1                       %                   push 1 for division
6 72 mul DataSize div   % 1 scalefactor     6*72/(Xmax-Xmin)
dup dup                 % 1 scalefactor scalefactor scalefactor
scale                   % 1 scalefactor
div                     % 1/scalefactor
currentlinewidth mul setlinewidth  %        adjust line width

Now, since this is a graph of a function, we don't actually need the tables of values. We can calculate values on the fly by evaluating the function.
/func { 3 add cos 3 div } def  % f(x) = cos(3+x)/3    define function y=f(x)

/Xini -5 def              % named loop control parameters
/Xfin 1 def
/Xstep 1 def
Xini dup dup func moveto  % moveto first point
Xstep add Xstep Xfin {    % x
    dup                   % x x
    func                  % x f(x)
    lineto                % line to next point
} for
stroke

Finally, if you can take the derivative of the function (create a function which calculates the slope of the original function at each point), then you can use my answer over on TeX.SE to draw the graph with many curve segments instead of lines.

