@code{(require 'eps-graph)}

@noindent
This is a graphing package creating encapsulated-PostScript files.
Its motivations and design choice are described in
@url{http://people.csail.mit.edu/jaffer/Docupage/grapheps}

@noindent
A dataset to be plotted is taken from a 2-dimensional array.
Corresponding coordinates are in rows.  Coordinates from any
pair of columns can be plotted.


@defun create-postscript-graph filename.eps size elt1 @dots{}

@var{filename.eps} should be a string naming an output file to be created.  @var{size}
should be an exact integer, a list of two exact integers, or #f.
@var{elt1}, ... are values returned by graphing primitives described here.

@code{create-postscript-graph} creates an @dfn{Encapsulated-PostScript} file named @var{filename.eps} containing
@cindex Encapsulated-PostScript
graphs as directed by the @var{elt1}, ... arguments.

The size of the graph is determined by the @var{size} argument.  If a list
of two integers, they specify the width and height.  If one integer,
then that integer is the width and the height is 3/4 of the width.
If #f, the graph will be 800 by 600.
@end defun

@noindent
These graphing procedures should be called as arguments to
@code{create-postscript-graph}.  The order of these arguments is
significant; PostScript graphics state is affected serially from the
first @var{elt} argument to the last.


@defun whole-page

Pushes a rectangle for the whole encapsulated page onto the
PostScript stack.  This pushed rectangle is an implicit argument to
@code{partition-page} or @code{setup-plot}.
@end defun

@menu
* Column Ranges::
* Drawing the Graph::
* Graphics Context::
* Rectangles::
* Legending::
* Legacy Plotting::
* Example Graph::
@end menu

@node Column Ranges, Drawing the Graph, PostScript Graphing, PostScript Graphing
@subsubsection Column Ranges

@noindent
A @dfn{range} is a list of two numbers, the minimum and the maximum.
@cindex range
@cindex range
Ranges can be given explicity or computed in PostScript by
@code{column-range}.


@defun column-range array k

Returns the range of values in 2-dimensional @var{array} column @var{k}.
@end defun


@defun pad-range range p

Expands @var{range} by @var{p}/100 on each end.
@end defun


@defun snap-range range

Expands @var{range} to round number of ticks.
@end defun


@defun combine-ranges range1 range2 @dots{}

Returns the minimal range covering all @var{range1}, @var{range2}, ...
@end defun


@defun setup-plot x-range y-range pagerect


@defunx setup-plot x-range y-range
@var{x-range} and @var{y-range} should each be a list of two numbers or the value returned
by @code{pad-range}, @code{snap-range}, or @code{combine-range}.
@var{pagerect} is the rectangle bounding the graph to be drawn; if missing, the
rectangle from the top of the PostScript stack is popped and used.

Based on the given ranges, @code{setup-plot} sets up scaling and margins for making
a graph.  The margins are sized proportional to the @var{fontheight}
value at the time of the call to setup-plot.  @code{setup-plot} sets two variables:

@table @var
@item plotrect
The region where data points will be plotted.
@item graphrect
The @var{pagerect} argument to @code{setup-plot}.  Includes plotrect, legends, etc.
@end table
@end defun

@node Drawing the Graph, Graphics Context, Column Ranges, PostScript Graphing
@subsubsection Drawing the Graph


@defun plot-column array x-column y-column proc3s

Plots points with x coordinate in @var{x-column} of @var{array} and y coordinate @var{y-column} of
@var{array}.  The symbol @var{proc3s} specifies the type of glyph or drawing style for
presenting these coordinates.
@end defun

@noindent
The glyphs and drawing styles available are:

@table @code
@item line
Draws line connecting points in order.
@item mountain
Fill area below line connecting points.
@item cloud
Fill area above line connecting points.
@item impulse
Draw line from x-axis to each point.
@item bargraph
Draw rectangle from x-axis to each point.
@item disc
Solid round dot.
@item point
Minimal point -- invisible if linewidth is 0.
@item square
Square box.
@item diamond
Square box at 45.o
@item plus
Plus sign.
@item cross
X sign.
@item triup
Triangle pointing upward
@item tridown
Triangle pointing downward
@item pentagon
Five sided polygon
@item circle
Hollow circle
@end table


@defun plot-text-column array x-column y-column t-column proc3s

Plots text in @var{t-column} of @var{array} at x coordinate in @var{x-column} of @var{array} and y coordinate
@var{y-column} of @var{array}.  The symbol @var{proc3s} specifies the offset of the text from the
specified coordinates.
@end defun

@noindent
The offsets available are:

@table @code
@item above
Draws the text centered above at the point.
@item center
Draws the text centered at the point.
@item below
Draws the text centered below the point.
@item left
Draws the text to the left of the point.
@item right
Draws the text to the right of the point.
@end table

All the offsets other than @code{center} are calculated to keep the
text clear of a glyph drawn at the same coordinates.  If you need
more or less clearance, use @code{set-glyphsize}.

@node Graphics Context, Rectangles, Drawing the Graph, PostScript Graphing
@subsubsection Graphics Context


@defun in-graphic-context arg @dots{}

Saves the current graphics state, executes @var{args}, then restores
to saved graphics state.
@end defun


@defun set-color color

@var{color} should be a string naming a Resene color, a saturate color, or a
number between 0 and 100.

@code{set-color} sets the PostScript color to the color of the given string, or a
grey value between black (0) and white (100).
@end defun


@defun set-font font height


@defunx set-font font encoding height
@var{font} should be a (case-sensitive) string naming a PostScript font.
@var{height} should be a positive real number.
@var{encoding} should name a PostScript encoding such as
@samp{ISOLatin1Encoding}.

@code{set-font} Changes the current PostScript font to @var{font} with the @var{encoding}
encoding, and height equal to @var{height}.  The default font is
@samp{Helvetica} (12pt).  The default encoding is
@samp{StandardEncoding}.
@end defun

@noindent
The base set of PostScript fonts is:

@multitable @columnfractions .20 .25 .25 .30
@item Times @tab Times-Italic @tab Times-Bold @tab Times-BoldItalic
@item Helvetica @tab Helvetica-Oblique @tab Helvetica-Bold @tab Helvetica-BoldOblique
@item Courier @tab Courier-Oblique @tab Courier-Bold @tab Courier-BoldOblique
@item Symbol
@end multitable

@noindent
The base set of PostScript encodings is:

@multitable @columnfractions .33 .33 .33
@item StandardEncoding @tab ISOLatin1Encoding @tab ExpertEncoding
@item ExpertSubsetEncoding @tab SymbolEncoding
@end multitable

@noindent
Line parameters do no affect fonts; they do effect glyphs.


@defun set-linewidth w

The default linewidth is 1.  Setting it to 0 makes the lines drawn
as skinny as possible.  Linewidth must be much smaller than
glyphsize for readable glyphs.
@end defun


@defun set-linedash j k

Lines are drawn @var{j}-on @var{k}-off.

@defunx set-linedash j
Lines are drawn @var{j}-on @var{j}-off.

@defunx set-linedash
Turns off dashing.
@end defun


@defun set-glyphsize w

Sets the (PostScript) variable glyphsize to @var{w}.  The default
glyphsize is 6.
@end defun

@noindent
The effects of @code{clip-to-rect} are also part of the graphic
context.

@node Rectangles, Legending, Graphics Context, PostScript Graphing
@subsubsection Rectangles

@noindent
A @dfn{rectangle} is a list of 4 numbers; the first two elements are
@cindex rectangle
the x and y coordinates of lower left corner of the rectangle.  The
other two elements are the width and height of the rectangle.


@defun whole-page

Pushes a rectangle for the whole encapsulated page onto the
PostScript stack.  This pushed rectangle is an implicit argument to
@code{partition-page} or @code{setup-plot}.
@end defun


@defun partition-page xparts yparts

Pops the rectangle currently on top of the stack and pushes @var{xparts} * @var{yparts}
sub-rectangles onto the stack in decreasing y and increasing x order.
If you are drawing just one graph, then you don't need @code{partition-page}.
@end defun


@defvar plotrect

The rectangle where data points should be plotted.  @var{plotrect} is set by
@code{setup-plot}.
@end defvar

@defvar graphrect

The @var{pagerect} argument of the most recent call to
@code{setup-plot}.  Includes plotrect, legends, etc.
@end defvar

@defun fill-rect rect

fills @var{rect} with the current color.
@end defun


@defun outline-rect rect

Draws the perimiter of @var{rect} in the current color.
@end defun


@defun clip-to-rect rect

Modifies the current graphics-state so that nothing will be drawn
outside of the rectangle @var{rect}.  Use @code{in-graphic-context} to limit
the extent of @code{clip-to-rect}.
@end defun

@node Legending, Legacy Plotting, Rectangles, PostScript Graphing
@subsubsection Legending


@defun title-top title subtitle


@defunx title-top title
Puts a @var{title} line and an optional @var{subtitle} line above the @code{graphrect}.
@end defun


@defun title-bottom title subtitle


@defunx title-bottom title
Puts a @var{title} line and an optional @var{subtitle} line below the @code{graphrect}.
@end defun


@defvar topedge
@defvarx bottomedge

These edge coordinates of @code{graphrect} are suitable for passing
as the first argument to @code{rule-horizontal}.
@end defvar

@defvar leftedge
@defvarx rightedge

These edge coordinates of @code{graphrect} are suitable for passing
as the first argument to @code{rule-vertical}.
@end defvar

@defun set-margin-templates left right

The margin-templates are strings whose displayed width is used to
reserve space for the left and right side numerical legends.
The default values are "-.0123456789".
@end defun


@defun rule-vertical x-coord text tick-width

Draws a vertical ruler with X coordinate @var{x-coord} and labeled with string
@var{text}.  If @var{tick-width} is positive, then the ticks are @var{tick-width} long on the right side
of @var{x-coord}; and @var{text} and numeric legends are on the left.  If @var{tick-width} is
negative, then the ticks are -@var{tick-width} long on the left side of @var{x-coord}; and @var{text}
and numeric legends are on the right.
@end defun


@defun rule-horizontal y-coord text tick-height

Draws a horizontal ruler with Y coordinate @var{y-coord} and labeled with
string @var{text}.  If @var{tick-height} is positive, then the ticks are @var{tick-height} long on the top
side of @var{y-coord}; and @var{text} and numeric legends are on the bottom.  If @var{tick-height} is
negative, then the ticks are -@var{tick-height} long on the bottom side of @var{y-coord}; and
@var{text} and numeric legends are on the top.
@end defun


@defun y-axis

Draws the y-axis.
@end defun


@defun x-axis

Draws the x-axis.
@end defun


@defun grid-verticals

Draws vertical lines through @code{graphrect} at each tick on the
vertical ruler.
@end defun


@defun grid-horizontals

Draws horizontal lines through @code{graphrect} at each tick on the
horizontal ruler.
@end defun

@node Legacy Plotting, Example Graph, Legending, PostScript Graphing
@subsubsection Legacy Plotting


@defvar graph:dimensions

A list of the width and height of the graph to be plotted using
@code{plot}.
@end defvar

@defun plot func x1 x2 npts


@defunx plot func x1 x2
Creates and displays using @code{(system "gv tmp.eps")} an
encapsulated PostScript graph of the function of one argument @var{func}
over the range @var{x1} to @var{x2}.  If the optional integer argument @var{npts} is
supplied, it specifies the number of points to evaluate @var{func} at.


@defunx plot x1 x2 npts func1 func2 @dots{}
Creates and displays an encapsulated PostScript graph of the
one-argument functions @var{func1}, @var{func2}, ... over the range
@var{x1} to @var{x2} at @var{npts} points.


@defunx plot coords x-label y-label
@var{coords} is a list or vector of coordinates, lists of x and y
coordinates.  @var{x-label} and @var{y-label} are strings with which
to label the x and y axes.
@end defun

@node Example Graph,  , Legacy Plotting, PostScript Graphing
@subsubsection Example Graph

@noindent
The file @file{am1.5.html}, a table of solar irradiance, is fetched
with @samp{wget} if it isn't already in the working directory.  The
file is read and stored into an array, @var{irradiance}.

@code{create-postscript-graph} is then called to create an
encapsulated-PostScript file, @file{solarad.eps}.  The size of the
page is set to 600 by 300.  @code{whole-page} is called and leaves
the rectangle on the PostScript stack.  @code{setup-plot} is called
with a literal range for x and computes the range for column 1.

Two calls to @code{top-title} are made so a different font can be
used for the lower half.  @code{in-graphic-context} is used to limit
the scope of the font change.  The graphing area is outlined and a
rule drawn on the left side.

Because the X range was intentionally reduced,
@code{in-graphic-context} is called and @code{clip-to-rect} limits
drawing to the plotting area.  A black line is drawn from data
column 1.  That line is then overlayed with a mountain plot of the
same column colored "Bright Sun".

After returning from the @code{in-graphic-context}, the bottom ruler
is drawn.  Had it been drawn earlier, all its ticks would have been
painted over by the mountain plot.

The color is then changed to @samp{seagreen} and the same graphrect
is setup again, this time with a different Y scale, 0 to 1000.  The
graphic context is again clipped to @var{plotrect}, linedash is set,
and column 2 is plotted as a dashed line.  Finally the rightedge is
ruled.  Having the line and its scale both in green helps
disambiguate the scales.

@example
(require 'eps-graph)
(require 'line-i/o)
(require 'string-port)

(define irradiance
  (let ((url "http://www.pv.unsw.edu.au/am1.5.html")
        (file "am1.5.html"))
    (define (read->list line)
      (define elts '())
      (call-with-input-string line
        (lambda (iprt) (do ((elt (read iprt) (read iprt)))
                           ((eof-object? elt) elts)
                         (set! elts (cons elt elts))))))
    (if (not (file-exists? file))
        (system (string-append "wget -c -O" file " " url)))
    (call-with-input-file file
      (lambda (iprt)
        (define lines '())
        (do ((line (read-line iprt) (read-line iprt)))
            ((eof-object? line)
             (let ((nra (make-array (A:floR64b)
                                      (length lines)
                                      (length (car lines)))))
               (do ((lns lines (cdr lns))
                    (idx (+ -1 (length lines)) (+ -1 idx)))
                   ((null? lns) nra)
                 (do ((kdx (+ -1 (length (car lines))) (+ -1 kdx))
                      (lst (car lns) (cdr lst)))
                     ((null? lst))
                   (array-set! nra (car lst) idx kdx)))))
          (if (and (positive? (string-length line))
                   (char-numeric? (string-ref line 0)))
              (set! lines (cons (read->list line) lines))))))))

(let ((xrange '(.25 2.5)))
  (create-postscript-graph
   "solarad.eps" '(600 300)
   (whole-page)
   (setup-plot xrange (column-range irradiance 1))
   (title-top
    "Solar Irradiance   http://www.pv.unsw.edu.au/am1.5.html")
   (in-graphic-context
    (set-font "Helvetica-Oblique" 12)
    (title-top
     ""
     "Key Centre for Photovoltaic Engineering UNSW - Air Mass 1.5 Global Spectrum"))
   (outline-rect plotrect)
   (rule-vertical leftedge "W/(m^2.um)" 10)
   (in-graphic-context (clip-to-rect plotrect)
                       (plot-column irradiance 0 1 'line)
                       (set-color "Bright Sun")
                       (plot-column irradiance 0 1 'mountain)
                       )
   (rule-horizontal bottomedge "Wavelength in .um" 5)
   (set-color 'seagreen)

   (setup-plot xrange '(0 1000) graphrect)
   (in-graphic-context (clip-to-rect plotrect)
                       (set-linedash 5 2)
                       (plot-column irradiance 0 2 'line))
   (rule-vertical rightedge "Integrated .W/(m^2)" -10)
   ))

(system "gv solarad.eps")
@end example
