fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
  SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_Chart.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_Chart.cxx 7903 2010-11-28 21:06:39Z matt $"
00003 //
00004 // Forms-compatible chart widget for the Fast Light Tool Kit (FLTK).
00005 //
00006 // Copyright 1998-2010 by Bill Spitzak and others.
00007 //
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of the GNU Library General Public
00010 // License as published by the Free Software Foundation; either
00011 // version 2 of the License, or (at your option) any later version.
00012 //
00013 // This library is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016 // Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public
00019 // License along with this library; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00021 // USA.
00022 //
00023 // Please report all bugs and problems on the following page:
00024 //
00025 //     http://www.fltk.org/str.php
00026 //
00027 
00028 #include <FL/math.h>
00029 #include <FL/Fl.H>
00030 #include <FL/Fl_Chart.H>
00031 #include <FL/fl_draw.H>
00032 #include "flstring.h"
00033 #include <stdlib.h>
00034 
00035 #define ARCINC  (2.0*M_PI/360.0)
00036 
00037 // this function is in fl_boxtype.cxx:
00038 void fl_rectbound(int x,int y,int w,int h, Fl_Color color);
00039 
00040 /* Widget specific information */
00041 
00042 static void draw_barchart(int x,int y,int w,int h,
00043                           int numb, FL_CHART_ENTRY entries[],
00044                           double min, double max, int autosize, int maxnumb,
00045                           Fl_Color textcolor)
00046 /* Draws a bar chart. x,y,w,h is the bounding box, entries the array of
00047    numb entries and min and max the boundaries. */
00048 {
00049   double incr;
00050   int zeroh;
00051   double lh = fl_height();
00052   if (max == min) incr = h;
00053   else incr = h/(max-min);
00054   if ( (-min*incr) < lh) {
00055       incr = (h - lh + min*incr)/(max-min);
00056       zeroh = int(y+h-lh);
00057   } else {
00058       zeroh = (int)rint(y+h+min * incr);
00059   }
00060   int bwidth = (int)rint(w/double(autosize?numb:maxnumb));
00061   /* Draw base line */
00062   fl_color(textcolor);
00063   fl_line(x, zeroh, x+w, zeroh);
00064   if (min == 0.0 && max == 0.0) return; /* Nothing else to draw */
00065   int i;
00066   /* Draw the bars */
00067   for (i=0; i<numb; i++) {
00068       int hh = (int)rint(entries[i].val*incr);
00069       if (hh < 0)
00070         fl_rectbound(x+i*bwidth,zeroh,bwidth+1,-hh+1, (Fl_Color)entries[i].col);
00071       else if (hh > 0)
00072         fl_rectbound(x+i*bwidth,zeroh-hh,bwidth+1,hh+1,(Fl_Color)entries[i].col);
00073   }
00074   /* Draw the labels */
00075   fl_color(textcolor);
00076   for (i=0; i<numb; i++)
00077       fl_draw(entries[i].str,
00078               x+i*bwidth+bwidth/2,zeroh,0,0,
00079               FL_ALIGN_TOP);
00080 }
00081 
00082 static void draw_horbarchart(int x,int y,int w,int h,
00083                              int numb, FL_CHART_ENTRY entries[],
00084                              double min, double max, int autosize, int maxnumb,
00085                              Fl_Color textcolor)
00086 /* Draws a horizontal bar chart. x,y,w,h is the bounding box, entries the
00087    array of numb entries and min and max the boundaries. */
00088 {
00089   int i;
00090   double lw = 0.0;              /* Maximal label width */
00091   /* Compute maximal label width */
00092   for (i=0; i<numb; i++) {
00093       double w1 = fl_width(entries[i].str);
00094       if (w1 > lw) lw = w1;
00095   }
00096   if (lw > 0.0) lw += 4.0;
00097   double incr;
00098   int zeroh;
00099   if (max == min) incr = w;
00100   else incr = w/(max-min);
00101   if ( (-min*incr) < lw) {
00102       incr = (w - lw + min*incr)/(max-min);
00103       zeroh = x+(int)rint(lw);
00104   } else {
00105       zeroh = (int)rint(x-min * incr);
00106   }
00107   int bwidth = (int)rint(h/double(autosize?numb:maxnumb));
00108   /* Draw base line */
00109   fl_color(textcolor);
00110   fl_line(zeroh, y, zeroh, y+h);
00111   if (min == 0.0 && max == 0.0) return; /* Nothing else to draw */
00112   /* Draw the bars */
00113   for (i=0; i<numb; i++) {
00114       int ww = (int)rint(entries[i].val*incr);
00115       if (ww > 0)
00116         fl_rectbound(zeroh,y+i*bwidth,ww+1,bwidth+1, (Fl_Color)entries[i].col);
00117       else if (ww < 0)
00118         fl_rectbound(zeroh+ww,y+i*bwidth,-ww+1,bwidth+1,(Fl_Color)entries[i].col);
00119   }
00120   /* Draw the labels */
00121   fl_color(textcolor);
00122   for (i=0; i<numb; i++)
00123       fl_draw(entries[i].str,
00124               zeroh-2,y+i*bwidth+bwidth/2,0,0,
00125               FL_ALIGN_RIGHT);
00126 }
00127 
00128 static void draw_linechart(int type, int x,int y,int w,int h,
00129                            int numb, FL_CHART_ENTRY entries[],
00130                            double min, double max, int autosize, int maxnumb,
00131                            Fl_Color textcolor)
00132 /* Draws a line chart. x,y,w,h is the bounding box, entries the array of
00133    numb entries and min and max the boundaries. */
00134 {
00135   int i;
00136   double lh = fl_height();
00137   double incr;
00138   if (max == min) incr = h-2.0*lh;
00139   else incr = (h-2.0*lh)/ (max-min);
00140   int zeroh = (int)rint(y+h-lh+min * incr);
00141   double bwidth = w/double(autosize?numb:maxnumb);
00142   /* Draw the values */
00143   for (i=0; i<numb; i++) {
00144       int x0 = x + (int)rint((i-.5)*bwidth);
00145       int x1 = x + (int)rint((i+.5)*bwidth);
00146       int yy0 = i ? zeroh - (int)rint(entries[i-1].val*incr) : 0;
00147       int yy1 = zeroh - (int)rint(entries[i].val*incr);
00148       if (type == FL_SPIKE_CHART) {
00149           fl_color((Fl_Color)entries[i].col);
00150           fl_line(x1, zeroh, x1, yy1);
00151       } else if (type == FL_LINE_CHART && i != 0) {
00152           fl_color((Fl_Color)entries[i-1].col);
00153           fl_line(x0,yy0,x1,yy1);
00154       } else if (type == FL_FILLED_CHART && i != 0) {
00155           fl_color((Fl_Color)entries[i-1].col);
00156           if ((entries[i-1].val>0.0)!=(entries[i].val>0.0)) {
00157               double ttt = entries[i-1].val/(entries[i-1].val-entries[i].val);
00158               int xt = x + (int)rint((i-.5+ttt)*bwidth);
00159               fl_polygon(x0,zeroh, x0,yy0, xt,zeroh);
00160               fl_polygon(xt,zeroh, x1,yy1, x1,zeroh);
00161           } else {
00162               fl_polygon(x0,zeroh, x0,yy0, x1,yy1, x1,zeroh);
00163           }
00164           fl_color(textcolor);
00165           fl_line(x0,yy0,x1,yy1);
00166       }
00167   }
00168   /* Draw base line */
00169   fl_color(textcolor);
00170   fl_line(x,zeroh,x+w,zeroh);
00171   /* Draw the labels */
00172   for (i=0; i<numb; i++)
00173       fl_draw(entries[i].str,
00174               x+(int)rint((i+.5)*bwidth), zeroh - (int)rint(entries[i].val*incr),0,0,
00175               entries[i].val>=0 ? FL_ALIGN_BOTTOM : FL_ALIGN_TOP);
00176 }
00177 
00178 static void draw_piechart(int x,int y,int w,int h,
00179                           int numb, FL_CHART_ENTRY entries[], int special,
00180                           Fl_Color textcolor)
00181 /* Draws a pie chart. x,y,w,h is the bounding box, entries the array of
00182    numb entries */
00183 {
00184   int i;
00185   double xc,yc,rad;     /* center and radius */
00186   double tot;           /* sum of values */
00187   double incr;          /* increment in angle */
00188   double curang;                /* current angle we are drawing */
00189   double txc,tyc;       /* temporary center */
00190   double lh = fl_height();
00191   /* compute center and radius */
00192   double h_denom = (special ? 2.3 : 2.0);
00193   rad = (h - 2*lh)/h_denom/1.1;
00194   xc = x+w/2.0; yc = y+h-1.1*rad-lh;
00195   /* compute sum of values */
00196   tot = 0.0;
00197   for (i=0; i<numb; i++)
00198     if (entries[i].val > 0.0) tot += entries[i].val;
00199   if (tot == 0.0) return;
00200   incr = 360.0/tot;
00201   /* Draw the pie */
00202   curang = 0.0;
00203   for (i=0; i<numb; i++)
00204     if (entries[i].val > 0.0)
00205     {
00206       txc = xc; tyc = yc;
00207       /* Correct for special pies */
00208       if (special && i==0)
00209       {
00210         txc += 0.3*rad*cos(ARCINC*(curang+0.5*incr*entries[i].val));
00211         tyc -= 0.3*rad*sin(ARCINC*(curang+0.5*incr*entries[i].val));
00212       }
00213       fl_color((Fl_Color)entries[i].col);
00214       fl_begin_polygon(); fl_vertex(txc,tyc);
00215       fl_arc(txc,tyc,rad,curang, curang+incr*entries[i].val);
00216       fl_end_polygon();
00217       fl_color(textcolor);
00218       fl_begin_loop(); fl_vertex(txc,tyc);
00219       fl_arc(txc,tyc,rad,curang, curang+incr*entries[i].val);
00220       fl_end_loop();
00221       curang += 0.5 * incr * entries[i].val;
00222       /* draw the label */
00223       double xl = txc + 1.1*rad*cos(ARCINC*curang);
00224       fl_draw(entries[i].str,
00225               (int)rint(xl),
00226               (int)rint(tyc - 1.1*rad*sin(ARCINC*curang)),
00227               0, 0,
00228               xl<txc ? FL_ALIGN_RIGHT : FL_ALIGN_LEFT);
00229       curang += 0.5 * incr * entries[i].val;
00230     }
00231 }
00232 
00233 void Fl_Chart::draw() {
00234 
00235     draw_box();
00236     Fl_Boxtype b = box();
00237     int xx = x()+Fl::box_dx(b); // was 9 instead of dx...
00238     int yy = y()+Fl::box_dy(b);
00239     int ww = w()-Fl::box_dw(b);
00240     int hh = h()-Fl::box_dh(b);
00241     fl_push_clip(xx, yy, ww, hh);
00242 
00243     ww--; hh--; // adjust for line thickness
00244 
00245     if (min >= max) {
00246         min = max = 0.0;
00247         for (int i=0; i<numb; i++) {
00248             if (entries[i].val < min) min = entries[i].val;
00249             if (entries[i].val > max) max = entries[i].val;
00250         }
00251     }
00252 
00253     fl_font(textfont(),textsize());
00254 
00255     switch (type()) {
00256     case FL_BAR_CHART:
00257         ww++; // makes the bars fill box correctly
00258         draw_barchart(xx,yy,ww,hh, numb, entries, min, max,
00259                         autosize(), maxnumb, textcolor());
00260         break;
00261     case FL_HORBAR_CHART:
00262         hh++; // makes the bars fill box correctly
00263         draw_horbarchart(xx,yy,ww,hh, numb, entries, min, max,
00264                         autosize(), maxnumb, textcolor());
00265         break;
00266     case FL_PIE_CHART:
00267         draw_piechart(xx,yy,ww,hh,numb,entries,0, textcolor());
00268         break;
00269     case FL_SPECIALPIE_CHART:
00270         draw_piechart(xx,yy,ww,hh,numb,entries,1,textcolor());
00271         break;
00272     default:
00273         draw_linechart(type(),xx,yy,ww,hh, numb, entries, min, max,
00274                         autosize(), maxnumb, textcolor());
00275         break;
00276     }
00277     draw_label();
00278     fl_pop_clip();
00279 }
00280 
00281 /*------------------------------*/
00282 
00283 #define FL_CHART_BOXTYPE        FL_BORDER_BOX
00284 #define FL_CHART_COL1           FL_COL1
00285 #define FL_CHART_LCOL           FL_LCOL
00286 #define FL_CHART_ALIGN          FL_ALIGN_BOTTOM
00287 
00294 Fl_Chart::Fl_Chart(int X, int Y, int W, int H,const char *L) :
00295 Fl_Widget(X,Y,W,H,L) {
00296   box(FL_BORDER_BOX);
00297   align(FL_ALIGN_BOTTOM);
00298   numb       = 0;
00299   maxnumb    = 0;
00300   sizenumb   = FL_CHART_MAX;
00301   autosize_  = 1;
00302   min = max  = 0;
00303   textfont_  = FL_HELVETICA;
00304   textsize_  = 10;
00305   textcolor_ = FL_FOREGROUND_COLOR;
00306   entries    = (FL_CHART_ENTRY *)calloc(sizeof(FL_CHART_ENTRY), FL_CHART_MAX + 1);
00307 }
00308 
00312 Fl_Chart::~Fl_Chart() {
00313   free(entries);
00314 }
00315 
00319 void Fl_Chart::clear() {
00320   numb = 0;
00321   min = max = 0;
00322   redraw();
00323 }
00324 
00332 void Fl_Chart::add(double val, const char *str, unsigned col) {
00333   /* Allocate more entries if required */
00334   if (numb >= sizenumb) {
00335     sizenumb += FL_CHART_MAX;
00336     entries = (FL_CHART_ENTRY *)realloc(entries, sizeof(FL_CHART_ENTRY) * (sizenumb + 1));
00337   }
00338   // Shift entries as needed
00339   if (numb >= maxnumb && maxnumb > 0) {
00340     memmove(entries, entries + 1, sizeof(FL_CHART_ENTRY) * (numb - 1));
00341     numb --;
00342   }
00343   entries[numb].val = float(val);
00344   entries[numb].col = col;
00345     if (str) {
00346         strlcpy(entries[numb].str,str,FL_CHART_LABEL_MAX + 1);
00347     } else {
00348         entries[numb].str[0] = 0;
00349     }
00350   numb++;
00351   redraw();
00352 }
00353 
00362 void Fl_Chart::insert(int ind, double val, const char *str, unsigned col) {
00363   int i;
00364   if (ind < 1 || ind > numb+1) return;
00365   /* Allocate more entries if required */
00366   if (numb >= sizenumb) {
00367     sizenumb += FL_CHART_MAX;
00368     entries = (FL_CHART_ENTRY *)realloc(entries, sizeof(FL_CHART_ENTRY) * (sizenumb + 1));
00369   }
00370   // Shift entries as needed
00371   for (i=numb; i >= ind; i--) entries[i] = entries[i-1];
00372   if (numb < maxnumb || maxnumb == 0) numb++;
00373   /* Fill in the new entry */
00374   entries[ind-1].val = float(val);
00375   entries[ind-1].col = col;
00376   if (str) {
00377       strlcpy(entries[ind-1].str,str,FL_CHART_LABEL_MAX+1);
00378   } else {
00379       entries[ind-1].str[0] = 0;
00380   }
00381   redraw();
00382 }
00383 
00392 void Fl_Chart::replace(int ind,double val, const char *str, unsigned col) {
00393   if (ind < 1 || ind > numb) return;
00394   entries[ind-1].val = float(val);
00395   entries[ind-1].col = col;
00396   if (str) {
00397       strlcpy(entries[ind-1].str,str,FL_CHART_LABEL_MAX+1);
00398   } else {
00399       entries[ind-1].str[0] = 0;
00400   }
00401   redraw();
00402 }
00403 
00408 void Fl_Chart::bounds(double a, double b) {
00409   this->min = a;
00410   this->max = b;
00411   redraw();
00412 }
00413 
00420 void Fl_Chart::maxsize(int m) {
00421   int i;
00422   /* Fill in the new number */
00423   if (m < 0) return;
00424   maxnumb = m;
00425   /* Shift entries if required */
00426   if (numb > maxnumb) {
00427       for (i = 0; i<maxnumb; i++)
00428           entries[i] = entries[i+numb-maxnumb];
00429       numb = maxnumb;
00430       redraw();
00431   }
00432 }
00433 
00434 //
00435 // End of "$Id: Fl_Chart.cxx 7903 2010-11-28 21:06:39Z matt $".
00436 //