Support mult-data charts and legend.
[pspp-builds.git] / src / output / charts / plot-chart.c
index 497251f07eda20677029b3066196a9aa13250a15..5641db1213be2cd070d66cffb7daf479823076f6 100644 (file)
@@ -1,21 +1,18 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 2004 Free Software Foundation, Inc.
-   Written by John Darrington <john@darrington.wattle.id.au>
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2009 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 of the
-   License, or (at your option) any later version.
+   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 3 of the License, 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.
+   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 program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
 
 
 
 #include <libpspp/str.h>
-#include <libpspp/alloc.h>
+#include <libpspp/assertion.h>
 #include <output/manager.h>
 #include <output/output.h>
 
+#include "xalloc.h"
 
-const char *data_colour[] = {
-  "brown",
-  "red",
-  "orange",
-  "yellow",
-  "green",
-  "blue",
-  "violet",
-  "grey",
-  "pink"
-};
+const char *const data_colour[N_CHART_COLOURS] =
+  {
+    "brown",
+    "red",
+    "orange",
+    "yellow",
+    "green",
+    "blue",
+    "violet",
+    "grey",
+    "pink"
+  };
 
 
 
@@ -58,9 +57,9 @@ const char *data_colour[] = {
    If label is non zero, then print it at the tick mark
 */
 void
-draw_tick(struct chart *chart, 
-         enum tick_orientation orientation, 
-         double position, 
+draw_tick(struct chart *chart,
+         enum tick_orientation orientation,
+         double position,
          const char *label, ...)
 {
   const int tickSize = 10;
@@ -71,12 +70,12 @@ draw_tick(struct chart *chart,
 
   pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
 
-  if ( orientation == TICK_ABSCISSA ) 
+  if ( orientation == TICK_ABSCISSA )
     pl_flinerel_r(chart->lp, position, 0, position, -tickSize);
-  else if (orientation == TICK_ORDINATE ) 
+  else if (orientation == TICK_ORDINATE )
       pl_flinerel_r(chart->lp, 0, position, -tickSize, position);
   else
-    assert(0);
+    NOT_REACHED ();
 
   if ( label ) {
     char buf[10];
@@ -84,9 +83,9 @@ draw_tick(struct chart *chart,
     va_start(ap,label);
     vsnprintf(buf,10,label,ap);
 
-    if ( orientation == TICK_ABSCISSA ) 
+    if ( orientation == TICK_ABSCISSA )
       pl_alabel_r(chart->lp, 'c','t', buf);
-    else if (orientation == TICK_ORDINATE ) 
+    else if (orientation == TICK_ORDINATE )
       {
        if ( fabs(position) < DBL_EPSILON )
            pl_moverel_r(chart->lp, 0, 10);
@@ -96,19 +95,19 @@ draw_tick(struct chart *chart,
 
     va_end(ap);
   }
-    
+
   pl_restorestate_r(chart->lp);
 }
 
 
 /* Write the title on a chart*/
-void  
+void
 chart_write_title(struct chart *chart, const char *title, ...)
 {
   va_list ap;
   char buf[100];
 
-  if ( ! chart ) 
+  if ( ! chart )
          return ;
 
   pl_savestate_r(chart->lp);
@@ -125,27 +124,27 @@ chart_write_title(struct chart *chart, const char *title, ...)
 
 
 /* Set the scale for the abscissa */
-void 
+void
 chart_write_xscale(struct chart *ch, double min, double max, int ticks)
 {
   double x;
 
-  const double tick_interval = 
+  const double tick_interval =
     chart_rounded_tick( (max - min) / (double) ticks);
 
   assert ( ch );
 
 
-  ch->x_max = ceil( max / tick_interval ) * tick_interval ; 
+  ch->x_max = ceil( max / tick_interval ) * tick_interval ;
   ch->x_min = floor ( min / tick_interval ) * tick_interval ;
 
 
-  ch->abscissa_scale = fabs(ch->data_right - ch->data_left) / 
+  ch->abscissa_scale = fabs(ch->data_right - ch->data_left) /
     fabs(ch->x_max - ch->x_min);
 
   for(x = ch->x_min ; x <= ch->x_max; x += tick_interval )
     {
-      draw_tick (ch, TICK_ABSCISSA, 
+      draw_tick (ch, TICK_ABSCISSA,
                 (x - ch->x_min) * ch->abscissa_scale, "%g", x);
     }
 
@@ -153,36 +152,36 @@ chart_write_xscale(struct chart *ch, double min, double max, int ticks)
 
 
 /* Set the scale for the ordinate */
-void 
+void
 chart_write_yscale(struct chart *ch, double smin, double smax, int ticks)
 {
   double y;
 
-  const double tick_interval = 
+  const double tick_interval =
     chart_rounded_tick( (smax - smin) / (double) ticks);
 
-  if ( !ch ) 
+  if ( !ch )
          return;
 
-  ch->y_max = ceil  ( smax / tick_interval ) * tick_interval ; 
+  ch->y_max = ceil  ( smax / tick_interval ) * tick_interval ;
   ch->y_min = floor ( smin / tick_interval ) * tick_interval ;
 
-  ch->ordinate_scale = 
+  ch->ordinate_scale =
     fabs(ch->data_top -  ch->data_bottom) / fabs(ch->y_max - ch->y_min) ;
 
   for(y = ch->y_min ; y <= ch->y_max; y += tick_interval )
     {
-    draw_tick (ch, TICK_ORDINATE, 
+    draw_tick (ch, TICK_ORDINATE,
               (y - ch->y_min) * ch->ordinate_scale, "%g", y);
     }
 }
 
 
 /* Write the abscissa label */
-void 
+void
 chart_write_xlabel(struct chart *ch, const char *label)
 {
-  if ( ! ch ) 
+  if ( ! ch )
     return ;
 
   pl_savestate_r(ch->lp);
@@ -197,10 +196,10 @@ chart_write_xlabel(struct chart *ch, const char *label)
 
 
 /* Write the ordinate label */
-void 
+void
 chart_write_ylabel(struct chart *ch, const char *label)
 {
-  if ( ! ch ) 
+  if ( ! ch )
     return ;
 
   pl_savestate_r(ch->lp);
@@ -211,3 +210,47 @@ chart_write_ylabel(struct chart *ch, const char *label)
 
   pl_restorestate_r(ch->lp);
 }
+
+
+void
+chart_write_legend (struct chart *ch)
+{
+  int i;
+  const int vstep = ch->font_size * 2;
+  const int xpad = 10;
+  const int ypad = 10;
+  const int swatch = 20;
+  const int legend_top = ch->data_top;
+  const int legend_bottom = legend_top -
+    (vstep * ch->n_datasets + 2 * ypad );
+
+  if ( ! ch )
+    return ;
+
+  pl_savestate_r (ch->lp);
+
+  pl_box_r (ch->lp, ch->legend_left, legend_top,
+           ch->legend_right - xpad, legend_bottom);
+
+  for (i = 0 ; i < ch->n_datasets ; ++i )
+    {
+      const int ypos = vstep * (i + 1);
+      const int xpos = ch->legend_left + xpad;
+      pl_move_r (ch->lp, xpos, legend_top - ypos);
+
+      pl_savestate_r(ch->lp);
+       pl_fillcolorname_r (ch->lp, data_colour [ i % N_CHART_COLOURS]);
+
+       pl_pentype_r (ch->lp, 1);
+       pl_filltype_r (ch->lp, 1);
+       pl_boxrel_r (ch->lp, 0, 0, swatch, swatch);
+
+
+      pl_moverel_r (ch->lp, swatch, 0);
+      pl_alabel_r (ch->lp, 0, 0, ch->dataset[i]);
+
+      pl_restorestate_r (ch->lp);
+    }
+
+  pl_restorestate_r (ch->lp);
+}