/*
* $Id: plot.c,v 1.28 2001/02/13 23:38:06 danny Exp $
*
* Copyright © 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* $Id: plot.c,v 1.28 2001/02/13 23:38:06 danny Exp $
*
* This file contains the code to draw plots from the Oleo data
* layered on top of the libsciplot functions.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WITH_DMALLOC
#include <dstdlib.h>
#endif
#include <ctype.h>
#include "global.h"
#include "graph.h"
#include "cmd.h"
#include "line.h"
#include "io-term.h"
#include "info.h"
#include "cell.h"
#include "regions.h"
#include "ref.h"
#include "io-utils.h"
#ifndef X_DISPLAY_MISSING
#include <X11/Intrinsic.h>
#endif
/*
* These are the devices supported by GNU plotutils.
* The first field is Oleo's representation of this file format.
* The second field is the string we need to pass to plotutils to select this
* type of device.
* The third field is the file name extension, should we save to a file.
* The last field is the string shown to the user to describe this device type.
*/
struct PlotutilsDevices PlotutilsDeviceArray[] = {
/*
{ GRAPH_NONE, "?", "?", "???" },
*/
{ GRAPH_POSTSCRIPT, "ps", "ps", "PostScript" },
{ GRAPH_TEK, "tek", "tek", "Tektronix" },
{ GRAPH_X, "x", "x", "X Window" },
{ GRAPH_PNG, "png", "png", "Portable Network Graphics" },
{ GRAPH_GIF, "gif", "gif", "GIF" },
{ GRAPH_METAFILE, "meta", "metafile", "GNU metafile" },
{ GRAPH_ILLUSTRATOR, "ai", "ai", "Adobe Illustrator" },
{ GRAPH_FIG, "fig", "fig", "Fig" },
{ GRAPH_PCL, "pcl", "pcl", "PCL" },
{ GRAPH_HPGL, "hpgl", "hpgl", "HP GL" },
{ GRAPH_REGIS, "regis", "regis", "ReGIS" },
{ GRAPH_PNM, "pnm", "pnm", "pnm" },
{ GRAPH_SVG, "svg", "svg", "svg" },
{ GRAPH_CGM, "cgm", "cgm", "cgm" },
/* Don't add anything after this line */
{ -1, NULL, NULL, NULL }
};
#include <stdarg.h>
typedef void (*sh) (int);
sh oldsig;
static RETSIGTYPE
handler(int sig)
{
signal(sig, handler);
io_error_msg("Plotutils: got signal %d, aborting", sig);
}
static void
SignalInit(void)
{
oleo_catch_signals(&handler);
#if 0
oldsig = signal(SIGSEGV, handler);
#endif
}
void
PlotInit(void)
{
if (! Global->PlotGlobal) {
Global->PlotGlobal = (struct PlotGlobalType *)malloc(sizeof(struct PlotGlobalType));
memset(Global->PlotGlobal, 0, sizeof(struct PlotGlobalType));
graph_set_axis_lo('x', "0.0");
graph_set_axis_hi('x', "100.0");
graph_set_axis_lo('y', "0.0");
graph_set_axis_hi('y', "100.0");
graph_set_axis_auto(0, 1); /* X auto */
graph_set_axis_auto(1, 1); /* Y auto */
graph_set_linetooffscreen(0);
Global->PlotGlobal->img_width = PLOT_WINDOW_WIDTH;
Global->PlotGlobal->img_height = PLOT_WINDOW_HEIGHT;
}
}
/*
* Don't compile this source unless you have GNU Plotutils
*/
#ifdef HAVE_LIBPLOT
#include <plot.h>
#include <oleo_plot.h>
#ifdef HAVE_LIBSCIPLOT
#include <sciplot/sciplot.h>
#else
#include <sciplot.h>
#endif
static plPlotter *handle;
static char *defaultcolor = "black";
static char *colors[] = { "yellow", "green", "blue", "red",
"magenta", "beige", "orange", "pink"};
static int ncolors = sizeof(colors) / sizeof(char *);
#ifdef HAVE_MOTIF
static Display *Sdpy;
static Window window;
#endif
/*
* Internal functions - initialize and close GNU PlotUtils
*/
static void
PuOpen(const char *plotter, FILE *outfile)
{
int r;
plPlotterParams *plotter_params;
SignalInit();
plotter_params = pl_newplparams();
#ifdef HAVE_MOTIF
/* This would crash plots into a file */
if (strcmp(plotter, "Xdrawable") == 0) {
pl_setplparam(plotter_params, "XDRAWABLE_DISPLAY", Sdpy);
pl_setplparam(plotter_params, "XDRAWABLE_DRAWABLE1", &window);
}
#endif
handle = pl_newpl_r(plotter, NULL, outfile, stderr, plotter_params);
r = pl_openpl_r(handle);
pl_deleteplparams(plotter_params);
pl_filltype_r(handle, 1);
pl_joinmod_r(handle, "round");
pl_flinewidth_r(handle, 2);
pl_pencolorname_r(handle, defaultcolor);
PlotInit();
}
#ifdef HAVE_MOTIF
void
PuX(Display *dpy, Window w)
{
Sdpy = dpy;
window = w;
}
#endif
static void
PuClose()
{
pl_deletepl_r(handle);
}
/*
* Get a float value out of a spreadsheet cell
*/
static double float_cell(CELL *cp)
{
char *s;
switch (GET_TYP (cp)) {
case TYP_INT:
return (double) (cp->cell_int);
case TYP_FLT:
return cp->cell_flt;
case TYP_STR:
s = cp->cell_str;
return astof (&s);
default:
return 0.;
}
}
/*
* Simple Pie Chart
*
* This function will create a pie chart on a given plotter.
* Some plotters put their output on a file stream.
* For this you can pass a file pointer.
*
* Oleo can handle an X plotter (NULL file), or e.g. a PostScript
* plotter (the file pointer is where the PostScript commands
* are sent).
*
* This code borrows from a piechart example by Bernhard Reiter.
*/
void
PuPieChart(char *plotter, FILE *outfile)
{
int i, num, c;
char *s, **labels;
double curr, incr, r;
CELL *cp;
struct rng rngx;
Point p;
Multigrapher *mg;
PuOpen(plotter, outfile);
mg = sp_create_plot(handle, SP_PLOT_PIE);
sp_begin_plot(mg, 1.0, 0.0, 0.0);
/* Title */
s = graph_get_title();
sp_set_title(mg, s);
memset(&p, 0, sizeof(Point));
/* Get the data labels */
rngx = graph_get_data(0);
num = 0;
make_cells_in_range(&rngx);
while ((cp = next_cell_in_range()))
num++;
labels = (char **)calloc(num, sizeof(char *));
rngx = graph_get_data(0);
i = 0;
make_cells_in_range(&rngx);
while ((cp = next_cell_in_range())) {
if (GET_TYP(cp) == TYP_STR)
labels[i] = strdup(cp->cell_str);
else if (GET_TYP(cp) == TYP_FLT)
labels[i] = strdup(flt_to_str(cp->cell_flt));
else
labels[i] = NULL;
i++;
}
/*
* Get the data points
*
* Do note that we're picking up all ranges of data points here,
* even if doing so doesn't really make sense.
*/
incr = curr = 0.0;
c = 0;
for (r = 1; r < NUM_DATASETS; r++) {
rngx = graph_get_data(r);
if (rngx.lr == 0 && rngx.lc == 0 && rngx.hr == 0 && rngx.hc == 0)
continue;
make_cells_in_range(&rngx);
i = 0;
while ((cp = next_cell_in_range())) {
p.label = labels[i];
p.x = float_cell(cp);
sp_plot_point(mg, &p);
i++;
}
}
for (i=0; i<num; i++)
if (labels[i])
free(labels[i]);
free(labels);
sp_end_plot(mg);
sp_destroy_plot(mg);
PuClose();
}
/*
* Stacked and unstacked bar charts
*/
void
PuBarChart(char *plotter, FILE *outfile)
{
int i, r;
struct rng rngx;
CELL *cp;
Multigrapher *mg;
PuOpen(plotter, outfile);
mg = sp_create_plot(handle, SP_PLOT_BAR);
sp_set_title(mg, graph_get_title());
sp_set_axis_title(mg, X_AXIS, graph_get_axis_title('x'));
sp_set_axis_title(mg, Y_AXIS, graph_get_axis_title('y'));
sp_begin_plot(mg, 1.0, 0.0, 0.0);
sp_first_dataset(mg);
for (r = 1; r < NUM_DATASETS; r++) {
rngx = graph_get_data(r);
if (rngx.lr == 0 && rngx.lc == 0 && rngx.hr == 0 && rngx.hc == 0)
continue;
sp_legend_label(mg, r, graph_get_data_title(r));
make_cells_in_range(&rngx);
i = 0;
while ((cp = next_cell_in_range())) {
sp_plot_point_simple(mg, 0, 0.0, float_cell(cp));
i++;
}
sp_next_dataset(mg);
}
sp_legend_draw(mg);
sp_end_plot(mg);
PuClose();
}
/*
* XY charts
*/
void
PuXYChart(char *plotter, FILE *outfile)
{
int i, r, num, nalloc, noxdata = 0;
double x, y, xmin, xmax, delta, *xes = 0,
ymax, ymin, oldx, oldy;
struct rng rngx;
CELL *cp;
Multigrapher *mg;
PuOpen(plotter, outfile);
/*
* Figure out whether dataset 0 has been specified.
* If it is, this is our X axis data; otherwise take a simple
* incremental counter for it. (1, 2, 3, 4, ...)
*/
rngx = graph_get_data(0);
if (rngx.lr == NON_ROW) {
noxdata = 1;
/* Figure out X axis borders - use the first dataset you find */
for (r = 1; r < NUM_DATASETS; r++) {
rngx = graph_get_data(r);
if (rngx.lr != NON_ROW)
break;
}
nalloc = 0;
make_cells_in_range(&rngx);
while ((cp = next_cell_in_range()))
nalloc++;
xmin = 1.0;
xmax = nalloc;
xes = NULL;
} else {
/* Figure out X axis borders */
rngx = graph_get_data(0);
make_cells_in_range(&rngx);
cp = next_cell_in_range();
xmin = xmax = float_cell(cp);
nalloc = 0;
while ((cp = next_cell_in_range())) {
y = float_cell(cp);
nalloc++;
if (y < xmin)
xmin = y;
else if (y > xmax)
xmax = y;
}
xes = (double *)calloc(nalloc + 1, sizeof(double));
make_cells_in_range(&rngx);
i = 0;
while ((cp = next_cell_in_range())) {
if (i > nalloc)
abort();
xes[i++] = float_cell(cp);
}
}
if (graph_get_axis_auto(0)) {
delta = xmax - xmin;
xmin -= delta * 0.1;
xmax += delta * 0.1;
} else {
xmin = graph_get_axis_lo(0);
xmax = graph_get_axis_lo(1);
}
num = 0;
if (graph_get_axis_auto(1)) {
for (r = 1; r < NUM_DATASETS; r++) {
rngx = graph_get_data(r);
if (rngx.lr == 0 && rngx.lc == 0 && rngx.hr == 0 && rngx.hc == 0)
continue;
make_cells_in_range(&rngx);
while ((cp = next_cell_in_range())) {
y = float_cell(cp);
num++;
if (y < ymin)
ymin = y;
else if (y > ymax)
ymax = y;
}
}
} else {
ymin = XYyMin; /* FIX ME */
ymax = XYyMax;
}
mg = sp_create_plot(handle, SP_PLOT_XY);
sp_set_title(mg, graph_get_title());
sp_set_axis_title(mg, X_AXIS, graph_get_axis_title('x'));
sp_set_axis_title(mg, Y_AXIS, graph_get_axis_title('y'));
sp_begin_plot(mg, 1.0, 0.0, 0.0);
#if 0
sp_plot_symbol(mg, 5);
#endif
sp_set_axis_range(mg, X_AXIS, xmin, xmax, 0.0, 0);
sp_set_axis_range(mg, Y_AXIS, ymin, ymax, 0.0, 0);
if (Global->PlotGlobal->ticktype[0] == SP_TICK_STRFTIME) {
/* protect against empty format string */
if (Global->PlotGlobal->tickformat[0] == NULL || strlen(Global->PlotGlobal->tickformat[0]) == 0)
sp_set_axis_ticktype_date(mg, X_AXIS, 1.0, 1.0, "%c");
else
sp_set_axis_ticktype_date(mg, X_AXIS, 1.0, 1.0, Global->PlotGlobal->tickformat[0]);
} else if (Global->PlotGlobal->ticktype[0] == SP_TICK_PRINTF) {
/* FIX ME need more API for this */
}
if (Global->PlotGlobal->ticktype[1] == SP_TICK_STRFTIME) {
/* protect against empty format string */
if (Global->PlotGlobal->tickformat[1] == NULL || strlen(Global->PlotGlobal->tickformat[1]) == 0)
sp_set_axis_ticktype_date(mg, Y_AXIS, 1.0, 1.0, "%c");
else
sp_set_axis_ticktype_date(mg, Y_AXIS, 1.0, 1.0, Global->PlotGlobal->tickformat[1]);
} else if (Global->PlotGlobal->ticktype[1] == SP_TICK_PRINTF) {
/* FIX ME need more API for this */
}
sp_draw_frame(mg, 1);
for (r = 1; r < NUM_DATASETS; r++) {
rngx = graph_get_data(r);
if (rngx.lr == 0 && rngx.lc == 0 && rngx.hr == 0 && rngx.hc == 0)
continue;
#if 0
make_cells_in_range(&rngx);
while ((cp = next_cell_in_range())) {
y = float_cell(cp);
num++;
if (y < ymin)
ymin = y;
else if (y > ymax)
ymax = y;
}
#endif
sp_plot_symbol(mg, 4 + r);
sp_legend_label(mg, r, graph_get_data_title(r));
if (! XYyAuto) {
ymin = XYyMin; /* FIX ME */
ymax = XYyMax;
}
/* sp_setcolor(mg, defaultcolor); */
make_cells_in_range(&rngx);
i = 0;
while ((cp = next_cell_in_range())) {
if (i > nalloc) {
abort();
}
if (xes)
x = xes[i];
else
x = (double)i;
y = float_cell(cp);
if (i > 0) {
double x1, x2, y1, y2;
int out1 = 0, out2 = 0;
x1 = 10.0 * (oldx - xmin) / (xmax - xmin);
y1 = 10.0 * (oldy - ymin) / (ymax - ymin);
x2 = 10.0 * (x - xmin) / (xmax - xmin);
y2 = 10.0 * (y - ymin) / (ymax - ymin);
if (x1 < 0.0 || x1 > 10.0 || y1 < 0.0 || y1 > 10.0)
out1 = 1;
if (x2 < 0.0 || x2 > 10.0 || y2 < 0.0 || y2 > 10.0)
out2 = 1;
if (Global->PlotGlobal->LineToOffscreen /* Always draw */
|| (out1 == 0 && out2 == 0)) {
sp_plot_point_simple(mg, 0, oldx, oldy);
sp_plot_point_simple(mg, 1, x, y);
}
}
oldx = x;
oldy = y;
i++;
}
sp_next_dataset(mg);
}
sp_legend_draw(mg);
sp_end_plot(mg);
sp_destroy_plot(mg);
PuClose();
if (xes)
free((void *)xes);
}
void PuPlot(enum graph_type gt, enum graph_device gd, FILE *fp)
{
void (*f)(char *, FILE *) = PuXYChart; /* FIX ME */
char *pu;
int i;
switch (gt) {
case GRAPH_XY:
f = PuXYChart;
break;
case GRAPH_BAR:
f = PuBarChart;
break;
case GRAPH_PIE:
f = PuPieChart;
break;
};
for (i=0, pu=NULL; PlotutilsDeviceArray[i].pus; i++) {
if (Global->PlotGlobal->device == PlotutilsDeviceArray[i].t) {
pu = PlotutilsDeviceArray[i].pus;
break;
}
}
if (! pu)
return; /* FIX ME */
PlotInit();
(*f)(pu, fp);
}
#endif /* HAVE_LIBPLOT */
syntax highlighted by Code2HTML, v. 0.9.1