/* graphs.c - produces graphs used by AWFFull $Id: graphs.c 250 2006-08-02 07:34:25Z steve $ Copyright (C) 1997-2001 Bradford L. Barrett (brad@mrunix.net) Copyright (C) 2004,2005 Stephen P. McInerney (spm@stedee.id.au) Copyright (C) 2006 by Benoit Rouits (brouits@free.fr) 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, and provided that the above copyright and permission notice is included with all distributed copies of this or derived software. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA This software uses the gd graphics library, which is copyright by Quest Protein Database Center, Cold Spring Harbor Labs. Please see the documentation supplied with the library for additional information and license terms, or visit www.boutell.com/gd/ for the most recent version of the library and supporting documentation. */ #include #include #include #include #include #include "awffull.h" /* Some systems don't define this */ #ifndef PI #define PI 3.14159265358979323846 #endif #define HITCOLOR hitcolor /* hits (green) */ #define FILECOLOR filecolor /* files (blue) */ #define SITECOLOR sitecolor /* sites (orange) */ #define KBYTECOLOR kbytecolor /* KBytes (red) */ #define PAGECOLOR pagecolor /* Files (cyan) */ #define VISITCOLOR visitcolor /* Visits (yellow) */ #define CX 156 /* center x (for pie) */ #define CY 150 /* center y (chart) */ #define XRAD 240 /* X-axis radius */ #define YRAD 200 /* Y-axis radius */ /* forward reference internal routines */ void init_graph(char *, int, int); struct pie_data *calc_arc(double, double, int, int, int, int); /* common public declarations */ const char *numchar[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31" }; gdImagePtr im; /* image buffer */ FILE *out; /* output file for PNG */ char maxvaltxt[32]; /* graph values */ double percent; /* percent storage */ u_long julday; /* julday value */ struct pie_data { int x; int y; /* line x,y */ int mx; int my; }; /* midpoint x,y */ /* colors */ int black, white, grey, dkgrey; int kbytecolor, filecolor, sitecolor, hitcolor, pagecolor, visitcolor; /*************************************************************** * ashex2int - ascii hexadecimal to integer converter * * does _not_ exactly do the same as strtoul() * * * * Returns a base-10 integer value from a 2 char * * ascii hexadecimal color number * * this integer is usable by th GD functions * * * ***************************************************************/ int ashex2int(const char *twocharstr) { int val; switch (twocharstr[1]) { case 'a':; case 'A': val = 10; break; case 'b':; case 'B': val = 11; break; case 'c':; case 'C': val = 12; break; case 'd':; case 'D': val = 13; break; case 'e':; case 'E': val = 14; break; case 'f':; case 'F': val = 15; break; default: val = (int) twocharstr[1] - 48; } switch (twocharstr[0]) { case 'a':; case 'A': val += 160; break; case 'b':; case 'B': val += 176; break; case 'c':; case 'C': val += 192; break; case 'd':; case 'D': val += 208; break; case 'e':; case 'E': val += 224; break; case 'f':; case 'F': val += 240; break; default: val += (int) (twocharstr[0] - 48) * 16; } return val; } /******************************************************************* * shortcuts to convert ascii hex color for gdImageColorAllocate() * *******************************************************************/ #define getred(s) (ashex2int((s[0] == '#')?s+1:s)) /* returns the red base-10 integer value from a html color */ #define getgreen(s) (ashex2int((s[0] == '#')?s+3:s+2)) /* returns the green base-10 integer value from a html color */ #define getblue(s) (ashex2int((s[0] == '#')?s+5:s+4)) /* returns the blue base-10 integer value from a html color */ /**************************************************************** * grid_spacing * * Calulate the grid spacing to use for a given range * * * * Heavily based on algorithims found in rrdtool-1.0.40 * * function: horizontal_grid * * * ****************************************************************/ double grid_spacing(unsigned long long range) { double range_f; /* covert the range to double */ double grid; /* grid value */ range_f = (double) range; grid = powf((double) 10, floorf(log10f(range_f))); /* Use of DBL_EPSILON to avoid double conversion problems */ if (grid <= DBL_EPSILON) { /* range is one -> 0.1 is reasonable scale */ grid = 0.1; } if ((range_f / grid) - 5.0 <= DBL_EPSILON) { grid /= 10.0; } if ((range_f / grid) - 10.0 > DBL_EPSILON) { grid *= 10.0; } if ((range_f / grid) - 5.0 <= DBL_EPSILON) { grid /= 5.0; } if ((range_f / grid) - 10.0 > DBL_EPSILON) { grid *= 5.0; } if ((range_f / grid) - 5.0 <= DBL_EPSILON) { grid /= 2.0; } VPRINT(VERBOSE5, _("grid: %f range/grid: %f\n"), grid, range_f / grid); return (grid); } /**************************************************************** * draw_horiz_grid * * Draws a series of horizontal grid lines * * * ****************************************************************/ int draw_horiz_grid(unsigned long long max, /* the max value */ int xleft, /* px. leftmost point */ int xright, /* px. rightmost point */ int ybottom, /* px. starting bottom value * * a line will NOT be drawn here */ int height, /* px. Height of the "frame" */ int text_offset, /* px. Offset for grid numbering Can be -'ve */ int is_kbytes /* Boolean. Set to 1 if unit is Kbytes - 1024 */ ) { double grid; /* the grid spacing to use */ double pct_offset; /* percentage offset from height for each grid line */ double i, j; double max_f = (double) max; /* max cast to double */ double height_f = (double) height; /* height cast to double */ int ytop = 0; /* y axis for current grid line */ char str[10]; int text_centring; /* Use to centre the Y Axis text against the grid lines */ char prefixes[9 + 1] = " kMGTPEZY"; /* Prefixes for bigger numbers * * see: http://physics.nist.gov/cuu/Units/prefixes.html */ int pref_idx = 0; int prev_text_end = ybottom; /* px. end of previous text */ double grid_cnt = 0.0; /* How many grid lines displayed. Used to avoid double probs */ VPRINT(VERBOSE4, "Draw Horiz Grid: Max: %llu\n", max); if (is_kbytes == 1) { j = max_f; /* Calculate new max */ while ((j - 1000.0 >= (DBL_EPSILON * (pref_idx + 1))) && (pref_idx <= 8)) { j /= 1000.0; pref_idx++; } max = max / ((pow(1024.0, (double) pref_idx))) * pow(1000.0, (double) pref_idx); max_f = (double) max; VPRINT(VERBOSE5, " Vol Horiz Grid: Max: %llu, Index: %d\n", max, pref_idx); } grid = grid_spacing(max); i = grid; while (i - max_f <= (DBL_EPSILON * grid_cnt)) { VPRINT(VERBOSE5, " i: %f maxval: %llu\n", i, max); VPRINT(VERBOSE5, "x1: %d x2: %d y: %d y1: %d pte: %d\n", xleft, xright, ybottom, ytop, prev_text_end); /* Calculate the prefixes to use */ grid_cnt++; j = i; pref_idx = 0; while ((j - 1000.0 >= (DBL_EPSILON * grid_cnt)) && (pref_idx <= 8)) { j /= 1000.0; pref_idx++; } if (j - floorf(j) <= (DBL_EPSILON * grid_cnt)) { if (pref_idx != 0) { snprintf(str, 10, "%.0f%c", j, prefixes[pref_idx]); } else { snprintf(str, 10, "%.0f", j); } } else { if (pref_idx != 0) { snprintf(str, 10, "%.1f%c", j, prefixes[pref_idx]); } else { snprintf(str, 10, "%.1f", j); } } /* Work out the line height */ pct_offset = (i / max_f) * height_f; ytop = ybottom - (int) pct_offset; gdImageLine(im, xleft, ytop, xright, ytop, dkgrey); text_centring = (int) ((((double) strlen(str) / 2.0) * 5.0) - 1.0); if (prev_text_end >= (ytop + text_centring)) { /* Final check to verify that we're not over-writing existing text */ gdImageStringUp(im, gdFontTiny, xleft - text_offset, ytop + text_centring, (unsigned char *) str, dkgrey); prev_text_end = ytop - text_centring - 7; } i += grid; VPRINT(VERBOSE5, "+ i: %f maxval: %llu max-i: %g eps: %g\n", i, max, max_f - i, DBL_EPSILON); } return (0); } /**************************************************************** * calc_bar_width * * Calculate the individual bar width to use * * * ****************************************************************/ int calc_bar_width(int drawing_width, /* The width of the drawing area */ int nbr_bars, /* How many bars will we be drawing */ int *bar_gap, /* The gap between bar groupings - white space */ int *bar_sep, /* The separation between individual bars in a grouping */ /* bar_gap and bar_sep, are ignored as incoming values - reset to the defaults * The new/set values of both will be passed back */ int nbr_grouped_bars /* The number of bars in a grouping */ ) { int bar_width = 1; /* The width to return */ VPRINT(VERBOSE5, "CalcBarWidth: DrawWidth: %d NbrBars: %d\n", drawing_width, nbr_bars); if (nbr_grouped_bars < 0) { ERRVPRINT(VERBOSE3, "Number bars in a group < 0. Bad News! Correcting.\n"); nbr_grouped_bars = 0; } /* If width gets too small, steadily drop bar_gap and bar_sep - till at most 1px each, and set width to 1px too * May have lotsa data points issues... FIXME! */ while (1) { /* bar_width is based on: * a. Size of drawing "window" -> main_width * minus left/right side gaps -> (bar_gap * 2) * b. How many bars to draw - ie divisions -> (idx_end - idx_start + 1 = index_diff + 1) * minus separation/gap between a group and individual bars -> (bar_gap + (bar_sep * 2)) * c. Can be left as int - want all bars to be same size! */ // bar_width = ((main_width - (bar_gap * 2)) / (index_diff + 1)) - (bar_gap + (bar_sep * 1)); bar_width = ((drawing_width - ((*bar_gap) * 2)) / (nbr_bars + 1)) - ((*bar_gap) + ((*bar_sep) * (nbr_grouped_bars - 1))); VPRINT(VERBOSE5, "CalcBarWidth: BarWidth: %d Gap: %d Sep: %d\n", bar_width, *bar_gap, *bar_sep); if ((nbr_grouped_bars == 1) && (bar_width > *bar_gap)) { /* exit when the separation is irrelevant and the barwidth is already ok */ break; } if ((bar_width > *bar_sep) && (bar_width > *bar_gap)) { /* exit once the bar width is actually bigger than the separation and the gap */ break; } /* If we can't get any smaller? Width is the smallest */ /* FIXME: width * nbrbars > drawing_width */ if ((*bar_gap == 1) && (*bar_sep == 1)) { bar_width = 1; break; } /* Decrease the Gap first, then the separation */ if (*bar_gap > 1) { (*bar_gap)--; } else { (*bar_sep)--; } } VPRINT(VERBOSE5, "CalcBarWidth: Returning BarWidth: %d\n", bar_width); return (bar_width); } /**************************************************************** * draw_bar * * Draw a single bar * * * ****************************************************************/ void draw_bar(int xleft, /* X Left */ int ybottom, /* Y Bottom of section */ int ytop, /* Y Top of section */ int bar_width, /* Bar Width */ double bar_percent, /* The percentage that this bar fills, Y-Axis */ int do_guide, /* Display a vertical guide bar; 1 == yes */ int colour /* Colour to display this bar in */ ) { int ybartop; /* 1st y location for drawing */ int xright; /* 2nd x location for drawing */ const int c_smallest_width = 2; /* px. Smallest width of a bar to ignore putting a box around, ie 3 or more to box. */ if (bar_percent > 0.0) { xright = xleft + bar_width - 1; ybartop = ybottom - (bar_percent * (ybottom - ytop - GRAPH_TB_IN_OFFSET)); if (ybartop > ybottom) { ybartop = ybottom; } if (do_guide == 1) { /* Put in a vertical bar for readability */ gdImageLine(im, xleft, ybottom, xleft, ytop, dkgrey); } gdImageFilledRectangle(im, xleft, ybartop, xright, ybottom, colour); /* Don't do the black outline if we're getting too thin */ if ((xright - xleft) > c_smallest_width) { gdImageRectangle(im, xleft, ybartop, xright, ybottom, black); } } } /**************************************************************** * * * YEAR_GRAPH6x - Year graph with six data sets * * Dependant on history_list being set * * idx_start & idx_end are index's into history_list * * * ****************************************************************/ int year_graph6x(char *fname, /* file name use */ char *title, /* title for graph */ int idx_start, /* begin start index */ int idx_end /* begin end index */ ) { /* local variables */ int i, j, k, k2; int xbarleft; /* Leftmost point for a given bar to draw */ int index_diff; /* Difference between start/end Use to set a maximum bar width */ int main_width; /* width of the primary section */ int main_height; /* Height of the primary section */ int main_bottom; /* Baseline of the primary section */ int section_left; /* Section break is here from left */ int section_right; /* End of Section from left, right postion */ int section_middle; /* Inner Section break is here from top */ int section_bottom; /* Bottom of vertical section line */ int bar_width; /* Width of the individual bars */ double bar_offset; /* basic offset of the individual bars */ const int c_base_bar_gap = 3; /* Constant. Set the default Bar Gap */ const int c_base_bar_sep = 3; /* Constant. Set the default Bar Separation */ int bar_gap = c_base_bar_gap; /* px. Space between bar groupings */ int bar_sep = c_base_bar_sep; /* px. Space between bars in a group */ int month_middle_x; /* Mid point offset between veritical bars */ int text_centring; /* Use to centre the Y Axis text against the grid lines */ int prev_text_end = 0; /* px. end of previous text. Used to print months on X Axis */ int cur_text_start; /* px. start of current text. Used to print months on X Axis */ int edge_text_offset; /* px. Offset from the edge for side text */ int font_char_width; /* px. Width of an individual char in a given font - magic constant */ int font_char_height; /* px. Height of an individual char in a given font - magic constant */ int do_year; u_long maxval = 1; double fmaxval = 0.0; /* initalize the graph */ init_graph(title, graph_index_x, graph_index_y); /* init as 512 x 256 */ /* Set up base calculations */ main_width = ((float) (graph_index_x - GRAPH_INNER_BOX_LEFT - (GRAPH_INNER_BOX_RIGHT + 1)) * GRAPH_INDEX_SPLIT); main_height = graph_index_y - GRAPH_INNER_BOX_TOP - GRAPH_INNER_BOX_BOTTOM - (GRAPH_TB_IN_OFFSET * 2) - 3; main_bottom = graph_index_y - GRAPH_INNER_BOX_BOTTOM - (GRAPH_TB_IN_OFFSET * 2); /* draw inner section lines */ section_left = main_width + GRAPH_INNER_BOX_LEFT + 2 + 1; section_right = graph_index_x - GRAPH_INNER_BOX_RIGHT - 2 - 1; /* 2 is width of inner box */ section_bottom = graph_index_y - GRAPH_INNER_BOX_BOTTOM - 2 - 1; /* 2 is width of inner box */ section_middle = (graph_index_y / 2) + 2; /* 2 is width of inner lines */ /* Vertical divide line */ gdImageLine(im, section_left, GRAPH_INNER_BOX_TOP, section_left, section_bottom, black); gdImageLine(im, section_left - 1, GRAPH_INNER_BOX_TOP, section_left - 1, section_bottom, white); /* Middle right line - horizontal */ gdImageLine(im, section_left, section_middle, section_right, section_middle, black); gdImageLine(im, section_left, section_middle - 1, section_right, section_middle - 1, white); /* Middle left line - horizontal */ gdImageLine(im, GRAPH_INNER_BOX_LEFT + 1, section_middle, section_left - 2, section_middle, black); gdImageLine(im, GRAPH_INNER_BOX_LEFT, section_middle - 1, section_left - 2, section_middle - 1, white); // gdImageLine (im, GRAPH_INNER_BOX_LEFT + 1, section_middle + 10, GRAPH_INNER_BOX_LEFT + main_width + 1, section_middle + 10, red); /* magic numbers */ font_char_width = 6; font_char_height = 10; if (graph_legend) { /* print color coded legends? */ k = font_char_width / 2; /* Half Char Sep */ k2 = font_char_width * 2; /* Half Space '/' Half Space */ /* Volume Legend */ i = (strlen(_("Volume")) * font_char_width); edge_text_offset = main_bottom + GRAPH_TEXT_Y_OFFSET; gdImageString(im, gdFontSmall, graph_index_x - GRAPH_INNER_BOX_LEFT - 1 - i, edge_text_offset + 1, (unsigned char *) _("Volume"), dkgrey); gdImageString(im, gdFontSmall, graph_index_x - GRAPH_INNER_BOX_LEFT - 2 - i, edge_text_offset, (unsigned char *) _("Volume"), KBYTECOLOR); /* Sites/Visits Legend */ i = (strlen(_("Visits")) * font_char_width); j = (strlen(_("Sites")) * font_char_width); edge_text_offset = GRAPH_INNER_BOX_TOP - font_char_height - GRAPH_TEXT_Y_OFFSET - 1; gdImageString(im, gdFontSmall, graph_index_x - GRAPH_INNER_BOX_LEFT - 1 - i - j - k2, edge_text_offset + 1, (unsigned char *) _("Visits"), dkgrey); gdImageString(im, gdFontSmall, graph_index_x - GRAPH_INNER_BOX_LEFT - 2 - i - j - k2, edge_text_offset, (unsigned char *) _("Visits"), VISITCOLOR); gdImageString(im, gdFontSmall, graph_index_x - GRAPH_INNER_BOX_LEFT - 1 - j - k2 + k, edge_text_offset + 1, (unsigned char *) "/", dkgrey); gdImageString(im, gdFontSmall, graph_index_x - GRAPH_INNER_BOX_LEFT - 2 - j - k2 + k, edge_text_offset, (unsigned char *) "/", black); gdImageString(im, gdFontSmall, graph_index_x - GRAPH_INNER_BOX_LEFT - 1 - j, edge_text_offset + 1, (unsigned char *) _("Sites"), dkgrey); gdImageString(im, gdFontSmall, graph_index_x - GRAPH_INNER_BOX_LEFT - 2 - j, edge_text_offset, (unsigned char *) _("Sites"), SITECOLOR); /* Hits/Files/Pages Legend */ i = (strlen(_("Pages")) * font_char_width); j = (strlen(_("Files")) * font_char_width); edge_text_offset = main_bottom + GRAPH_TEXT_Y_OFFSET; gdImageString(im, gdFontSmall, section_left + 1, edge_text_offset + 1, (unsigned char *) _("Pages"), dkgrey); gdImageString(im, gdFontSmall, section_left, edge_text_offset, (unsigned char *) _("Pages"), PAGECOLOR); gdImageString(im, gdFontSmall, section_left + i + k + 1, edge_text_offset + 1, (unsigned char *) "/", dkgrey); gdImageString(im, gdFontSmall, section_left + i + k, edge_text_offset, (unsigned char *) "/", black); gdImageString(im, gdFontSmall, section_left + i + k2 + 1, edge_text_offset + 1, (unsigned char *) _("Files"), dkgrey); gdImageString(im, gdFontSmall, section_left + i + k2, edge_text_offset, (unsigned char *) _("Files"), FILECOLOR); gdImageString(im, gdFontSmall, section_left + i + j + k2 + k + 1, edge_text_offset + 1, (unsigned char *) "/", dkgrey); gdImageString(im, gdFontSmall, section_left + i + j + k2 + k, edge_text_offset, (unsigned char *) "/", black); gdImageString(im, gdFontSmall, section_left + i + j + k2 * 2 + 1, edge_text_offset + 1, (unsigned char *) _("Hits"), dkgrey); gdImageString(im, gdFontSmall, section_left + i + j + k2 * 2, edge_text_offset, (unsigned char *) _("Hits"), HITCOLOR); } /**************************************************** * Do the main Section * * Hits, Files * ****************************************************/ /* get max val */ for (i = idx_start; i < idx_end; i++) { if (history_list[i].hit > maxval) maxval = history_list[i].hit; if (history_list[i].file > maxval) maxval = history_list[i].file; } if (maxval <= 0) { maxval = 1; } if (graph_lines) { edge_text_offset = 6 + GRAPH_TEXT_Y_OFFSET + 1; /* tiny font has height of 6 */ draw_horiz_grid(maxval, GRAPH_INNER_BOX_LEFT + 1, GRAPH_INNER_BOX_LEFT + main_width + 1, main_bottom, section_bottom - section_middle - GRAPH_TB_IN_OFFSET - 1, edge_text_offset, 0); } /* width/offset calculations */ index_diff = idx_end - idx_start; if (index_diff < GRAPH_INDEX_MIN_BARS) { /* Set the minimum # of bars */ index_diff = GRAPH_INDEX_MIN_BARS; } bar_width = calc_bar_width(main_width, index_diff, &bar_gap, &bar_sep, 2); bar_offset = (((double) main_width - ((double) bar_gap * 2.0)) / (double) index_diff); /* bar_offset is based on: * a. Size of drawing "window" -> G_YEAR_MAIN_WIDTH * minus left/right side gaps -> (bar_gap * 2) * b. How many bars to draw -> (idx_end - idx_start) * We start from zero, so no need to "+ 1" * c. Must double, else we lose significant fractions and bunch up leftwards - yuk. */ month_middle_x = (bar_width + bar_sep * 1) / 2 + 1; /* Calc for the mid point on which to then draw the month on the X Axis */ // fprintf (stderr, "start: %d end: %d diff: %d idx: %d width: %d offset: %f mainwidth: %d\n", idx_start, idx_end, index_diff, idx_end - idx_start, bar_width, bar_offset, main_width); for (i = idx_start; i < idx_end; i++) { /* HITS */ percent = ((double) history_list[i].hit / (double) maxval); if ((history_list[i].month == 1) && (index_diff > 12)) { do_year = 1; } else { do_year = 0; } xbarleft = GRAPH_INNER_BOX_LEFT + GRAPH_LR_IN_OFFSET + bar_gap + ((i - idx_start) * bar_offset); draw_bar(xbarleft, main_bottom, section_middle + 1, bar_width, percent, do_year, HITCOLOR); /* X Axis */ text_centring = (((double) strlen(s_month[history_list[i].month - 1])) / 2.0) * (double) font_char_width; /* Probably overkill - should always be 3 for the strlen */ cur_text_start = xbarleft + month_middle_x - text_centring; if (prev_text_end < cur_text_start) { /* Final check to verify that we're not over-writing existing text */ gdImageString(im, gdFontSmall, cur_text_start, main_bottom + GRAPH_TEXT_Y_OFFSET, (unsigned char *) s_month[history_list[i].month - 1], black); prev_text_end = xbarleft + month_middle_x + text_centring + (font_char_width / 2); } /* FILES */ percent = ((double) history_list[i].file / (double) maxval); draw_bar(xbarleft + bar_sep, main_bottom, section_middle + 1, bar_width, percent, 0, FILECOLOR); } /**************************************************** * Do the Top Left Section * * Pages * ****************************************************/ /* get max val */ maxval = 0; for (i = idx_start; i < idx_end; i++) { if (history_list[i].page > maxval) maxval = history_list[i].page; } if (maxval <= 0) maxval = 1; if (graph_lines) { edge_text_offset = 6 + GRAPH_TEXT_Y_OFFSET + 1; draw_horiz_grid(maxval, GRAPH_INNER_BOX_LEFT + 1, GRAPH_INNER_BOX_LEFT + main_width + 1, section_middle, section_middle - GRAPH_INNER_BOX_TOP - GRAPH_TB_IN_OFFSET - 1, edge_text_offset, 0); } bar_width = calc_bar_width(main_width, index_diff, &bar_gap, &bar_sep, 1); for (i = idx_start; i < idx_end; i++) { /* PAGES */ percent = ((double) history_list[i].page / (double) maxval); if ((history_list[i].month == 1) && (index_diff > 12)) { do_year = 1; } else { do_year = 0; } draw_bar(GRAPH_INNER_BOX_LEFT + GRAPH_LR_IN_OFFSET + bar_gap + ((i - idx_start) * bar_offset), section_middle - GRAPH_TB_IN_OFFSET - 1, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, do_year, PAGECOLOR); } /**************************************************** * Do the Top Right Section * * Visits, Sites * ****************************************************/ /* get max val */ maxval = 0; for (i = idx_start; i < idx_end; i++) { if (history_list[i].site > maxval) maxval = history_list[i].site; if (history_list[i].visit > maxval) maxval = history_list[i].visit; } if (maxval <= 0) maxval = 1; if (graph_lines) { edge_text_offset = section_left - section_right - GRAPH_TEXT_Y_OFFSET; draw_horiz_grid(maxval, section_left + 1, section_right, section_middle, section_middle - GRAPH_INNER_BOX_TOP - GRAPH_TB_IN_OFFSET - 1, edge_text_offset, 0); } bar_gap = c_base_bar_gap; bar_sep = c_base_bar_sep; bar_width = calc_bar_width(section_right - section_left, index_diff, &bar_gap, &bar_sep, 2); bar_offset = ((double) (section_right - section_left) - ((double) bar_gap * 2.0)) / (double) index_diff; for (i = idx_start; i < idx_end; i++) { /* VISITS */ percent = ((double) history_list[i].visit / (double) maxval); if ((history_list[i].month == 1) && (index_diff > 12)) { do_year = 1; } else { do_year = 0; } xbarleft = section_left + 2 + bar_gap + ((i - idx_start) * bar_offset); draw_bar(xbarleft, section_middle - GRAPH_TB_IN_OFFSET - 1, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, do_year, VISITCOLOR); /* SITES */ percent = ((double) history_list[i].site / (double) maxval); draw_bar(xbarleft + bar_sep, section_middle - GRAPH_TB_IN_OFFSET - 1, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, 0, SITECOLOR); } /**************************************************** * Do the Bottom Right Section * * Volume * ****************************************************/ /* Calculate maxval */ fmaxval = 0.0; for (i = idx_start; i < idx_end; i++) { if (history_list[i].xfer > fmaxval) fmaxval = history_list[i].xfer; } if (fmaxval <= 0.0) fmaxval = 1.0; if (graph_lines) { edge_text_offset = section_left - section_right - GRAPH_TEXT_Y_OFFSET; draw_horiz_grid((unsigned long long) fmaxval * 1024, section_left + 1, section_right, section_bottom, section_bottom - section_middle - GRAPH_TB_IN_OFFSET - 1, edge_text_offset, 1); } bar_width = calc_bar_width(section_right - section_left, index_diff, &bar_gap, &bar_sep, 1); for (i = idx_start; i < idx_end; i++) { /* VOLUME */ percent = ((double) history_list[i].xfer / (double) fmaxval); if ((history_list[i].month == 1) && (index_diff > 12)) { do_year = 1; } else { do_year = 0; } draw_bar(section_left + 2 + bar_gap + ((i - idx_start) * bar_offset), main_bottom, section_middle + 1, bar_width, percent, do_year, KBYTECOLOR); } /* save png image */ if ((out = fopen(fname, "wb")) != NULL) { gdImagePng(im, out); fclose(out); } /* deallocate memory */ gdImageDestroy(im); return (0); } /***************************************************************** * * * MONTH_GRAPH6 - Month graph with six data sets * * * *****************************************************************/ //#define YSIZE 400 int month_graph6(char *fname, /* filename */ char *title, /* graph title */ int month, /* graph month */ int year, /* graph year */ u_long data1[31], /* data1 (hits) */ u_long data2[31], /* data2 (files) */ u_long data3[31], /* data3 (sites) */ unsigned long long data4[31], /* data4 (kbytes) */ u_long data5[31], /* data5 (views) */ u_long data6[31]) { /* data6 (visits) */ /* local variables */ int i, j, k, k2; u_long maxval = 0; double fmaxval = 0.0; int xbarleft; int main_width; /* width of the primary section */ int height_upper; /* Height of the hits/pages/files section */ int height_middle; /* Height of the visits/sites section */ int height_lower; /* Height of the volume section */ int bottom_upper; int bottom_middle; int bottom_lower; int day_middle_x; /* px. Mid point offset between veritical bars */ int text_centring; /* px. Use to centre the Y Axis text against the grid lines */ int cur_text_start; /* px. start of current text. Used to print months on X Axis */ int section_left; /* Section break is here from left */ int section_right; /* End of Section from left, right postion */ int section_middle; /* Inner Section break is here from top */ int section_bottom; /* Bottom of vertical section line */ int bar_width; /* Width of the individual bars */ double bar_offset; /* basic offset of the individual bars */ int bar_gap = 2; /* px. Space between bar groupings */ int bar_sep = 2; /* px. Space between bars in a group */ int index_diff; /* Difference between start/end Use to set a maximum bar width */ int font_char_width; /* px. Width of an individual char in a given font - magic constant */ int font_char_height; /* px. Height of an individual char in a given font - magic constant */ int edge_text_offset = 0; /* px. Offset from the edge for side text */ unsigned char text_separator[2] = "/"; /* Just a simple var to hold the char to separate words. Unsigned for GD */ /* calc julian date for month */ julday = (jdate(1, month, year) % 7); /* initalize the graph */ init_graph(title, graph_daily_x, graph_daily_y); /* draw inner section lines */ section_left = GRAPH_INNER_BOX_LEFT; section_right = graph_daily_x - GRAPH_INNER_BOX_RIGHT - 2 - 1; /* 2 is width of inner box */ section_middle = ((graph_daily_y - GRAPH_INNER_BOX_TOP - GRAPH_INNER_BOX_BOTTOM) * GRAPH_DAILY_SPLIT_MAIN) + GRAPH_INNER_BOX_TOP; section_bottom = ((graph_daily_y - GRAPH_INNER_BOX_TOP - GRAPH_INNER_BOX_BOTTOM) * GRAPH_DAILY_SPLIT_2NDRY) + section_middle; gdImageLine(im, section_left, section_middle, section_right, section_middle, black); gdImageLine(im, section_left - 1, section_middle - 1, section_right, section_middle - 1, white); gdImageLine(im, section_left, section_bottom, section_right, section_bottom, black); gdImageLine(im, section_left, section_bottom - 1, section_right, section_bottom - 1, white); /* Set up base calculations */ main_width = graph_daily_x - (GRAPH_INNER_BOX_LEFT + 1) - (GRAPH_INNER_BOX_RIGHT + 1) - 2; height_upper = section_middle - GRAPH_INNER_BOX_TOP - (GRAPH_TB_IN_OFFSET * 2) - 3; bottom_upper = section_middle - (GRAPH_TB_IN_OFFSET * 2); height_middle = section_bottom - section_middle - (GRAPH_TB_IN_OFFSET * 2) - 3; bottom_middle = section_bottom - (GRAPH_TB_IN_OFFSET * 2); height_lower = graph_daily_y - GRAPH_INNER_BOX_BOTTOM - section_bottom - (GRAPH_TB_IN_OFFSET * 2) - 3; bottom_lower = graph_daily_y - GRAPH_INNER_BOX_BOTTOM - (GRAPH_TB_IN_OFFSET * 2); // gdImageLine (im, GRAPH_INNER_BOX_LEFT + 1, section_middle + 15, GRAPH_INNER_BOX_LEFT + 1 + main_width, section_middle + 15, red); /* magic numbers */ font_char_width = 6; font_char_height = 10; if (graph_legend) { /* print color coded legends? */ /* Volume Legend */ /* i = (strlen (_("Volume")) * font_char_width); */ edge_text_offset = graph_daily_x - GRAPH_INNER_BOX_RIGHT; /* + GRAPH_TEXT_X_OFFSET; */ gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, graph_daily_y - GRAPH_INNER_BOX_BOTTOM - GRAPH_TEXT_Y_OFFSET - 1, (unsigned char *) _("Volume"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, graph_daily_y - GRAPH_INNER_BOX_BOTTOM - GRAPH_TEXT_Y_OFFSET, (unsigned char *) _("Volume"), KBYTECOLOR); /* Sites/Visits Legend */ i = (strlen(_("Sites")) * font_char_width); k = font_char_width / 2; /* Half Char Sep */ k2 = font_char_width * 2; /* Half Space '/' Half Space */ /* j = (strlen (_("Sites")) * font_char_width); */ gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, section_bottom - GRAPH_TEXT_Y_OFFSET - 1, (unsigned char *) _("Sites"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, section_bottom - GRAPH_TEXT_Y_OFFSET, (unsigned char *) _("Sites"), SITECOLOR); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, section_bottom - GRAPH_TEXT_Y_OFFSET - i - k - 1, (unsigned char *) text_separator, dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, section_bottom - GRAPH_TEXT_Y_OFFSET - i - k, (unsigned char *) text_separator, black); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, section_bottom - GRAPH_TEXT_Y_OFFSET - i - k2 - 1, (unsigned char *) _("Visits"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, section_bottom - GRAPH_TEXT_Y_OFFSET - i - k2, (unsigned char *) _("Visits"), VISITCOLOR); /* Hits/Files/Pages Legend */ i = (strlen(_("Pages")) * font_char_width); j = (strlen(_("Files")) * font_char_width); /* k same as above */ gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, section_middle - GRAPH_TEXT_Y_OFFSET - 1, (unsigned char *) _("Pages"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, section_middle - GRAPH_TEXT_Y_OFFSET, (unsigned char *) _("Pages"), PAGECOLOR); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, section_middle - GRAPH_TEXT_Y_OFFSET - i - k - 1, (unsigned char *) text_separator, dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, section_middle - GRAPH_TEXT_Y_OFFSET - i - k, (unsigned char *) text_separator, black); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, section_middle - GRAPH_TEXT_Y_OFFSET - i - k2 - 1, (unsigned char *) _("Files"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, section_middle - GRAPH_TEXT_Y_OFFSET - i - k2, (unsigned char *) _("Files"), FILECOLOR); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, section_middle - GRAPH_TEXT_Y_OFFSET - i - j - k2 - k - 1, (unsigned char *) text_separator, dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, section_middle - GRAPH_TEXT_Y_OFFSET - i - j - k2 - k - 1, (unsigned char *) text_separator, black); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, section_middle - GRAPH_TEXT_Y_OFFSET - i - j - k2 * 2 - 1, (unsigned char *) _("Hits"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, section_middle - GRAPH_TEXT_Y_OFFSET - i - j - k2 * 2 - 1, (unsigned char *) _("Hits"), HITCOLOR); } /**************************************************** * Do the Upper Section * * Hits, Files, Pages * ****************************************************/ /* get max val */ for (i = 0; i < 31; i++) { if (data1[i] > maxval) maxval = data1[i]; if (data2[i] > maxval) maxval = data2[i]; if (data5[i] > maxval) maxval = data5[i]; } if (maxval <= 0) maxval = 1; if (graph_lines) { edge_text_offset = 6 + GRAPH_TEXT_Y_OFFSET + 1; /* tiny font has height of 6 */ draw_horiz_grid(maxval, section_left + 1, section_right, bottom_upper, height_upper, edge_text_offset, 0); } /* width/offset calculations */ index_diff = 31; bar_width = calc_bar_width(main_width, index_diff, &bar_gap, &bar_sep, 3); bar_offset = (((double) main_width - ((double) bar_gap * 2.0)) / (double) index_diff); day_middle_x = (bar_width + bar_sep * 2) / 2 + 1; /* Calc for the mid point on which to then draw the month on the X Axis */ for (i = 0; i < 31; i++) { /* HITS */ percent = ((double) data1[i] / (double) maxval); xbarleft = GRAPH_INNER_BOX_LEFT + GRAPH_LR_IN_OFFSET + bar_gap + (i * bar_offset); draw_bar(xbarleft, bottom_upper, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, 0, HITCOLOR); /* X Axis */ text_centring = (((double) strlen(numchar[i + 1])) / 2.0) * (double) font_char_width; cur_text_start = xbarleft + day_middle_x - text_centring; /* fprintf (stderr, "textcent: %d start: %d midx: %d bottom: %d\n", text_centring, cur_text_start, day_middle_x, bottom_lower + GRAPH_TEXT_Y_OFFSET); */ if ((julday % 7 == 6) || (julday % 7 == 0)) /* Change colour for Sat/Sun */ gdImageString(im, gdFontSmall, cur_text_start, bottom_lower + GRAPH_TEXT_Y_OFFSET, (unsigned char *) numchar[i + 1], HITCOLOR); else gdImageString(im, gdFontSmall, cur_text_start, bottom_lower + GRAPH_TEXT_Y_OFFSET, (unsigned char *) numchar[i + 1], black); julday++; /* FILES */ percent = ((double) data2[i] / (double) maxval); draw_bar(xbarleft + bar_sep, bottom_upper, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, 0, FILECOLOR); /* PAGES */ percent = ((double) data5[i] / (double) maxval); draw_bar(xbarleft + bar_sep + bar_sep, bottom_upper, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, 0, PAGECOLOR); } /**************************************************** * Do the Middle Section * * Sites, Visits * ****************************************************/ /* get max val */ maxval = 0; for (i = 0; i < 31; i++) { if (data3[i] > maxval) maxval = data3[i]; if (data6[i] > maxval) maxval = data6[i]; } if (maxval <= 0) maxval = 1; if (graph_lines) { /* re-use edge_text_offset from previous calc */ draw_horiz_grid(maxval, section_left + 1, section_right, bottom_middle, height_middle, edge_text_offset, 0); } bar_width = calc_bar_width(main_width, index_diff, &bar_gap, &bar_sep, 2); for (i = 0; i < 31; i++) { /* Visits */ percent = ((double) data6[i] / (double) maxval); xbarleft = GRAPH_INNER_BOX_LEFT + GRAPH_LR_IN_OFFSET + bar_gap + (i * bar_offset); draw_bar(xbarleft, bottom_middle, bottom_middle - height_middle, bar_width, percent, 0, VISITCOLOR); /* Sites */ percent = ((double) data3[i] / (double) maxval); draw_bar(xbarleft + bar_sep, bottom_middle, bottom_middle - height_middle, bar_width, percent, 0, SITECOLOR); } /**************************************************** * Do the Lower Section * * Volume Transferred * ****************************************************/ /* get max val */ fmaxval = 0.0; for (i = 0; i < 31; i++) { if (data4[i] > fmaxval) fmaxval = data4[i]; } if (fmaxval <= 0.0) fmaxval = 1.0; if (graph_lines) { /* re-use edge_text_offset from previous calc */ VPRINT(VERBOSE5, "Draw Vol Grid: Max: %f, x1: %d, x2: %d, y: %d, hgt: %d\n", fmaxval, section_left + 1, section_right, bottom_lower, height_lower); draw_horiz_grid((unsigned long long) fmaxval, section_left + 1, section_right, bottom_lower, height_lower, edge_text_offset, 1); } bar_width = calc_bar_width(main_width, index_diff, &bar_gap, &bar_sep, 2); for (i = 0; i < 31; i++) { /* VOLUME */ percent = ((double) data4[i] / (double) fmaxval); xbarleft = GRAPH_INNER_BOX_LEFT + GRAPH_LR_IN_OFFSET + bar_gap + (i * bar_offset); draw_bar(xbarleft, bottom_lower, bottom_lower - height_lower, bar_width, percent, 0, KBYTECOLOR); } /* open file for writing */ if ((out = fopen(fname, "wb")) != NULL) { gdImagePng(im, out); fclose(out); } /* deallocate memory */ gdImageDestroy(im); return (0); } /***************************************************************** * * * DAY_GRAPH4 - Day graph with four data sets * * * *****************************************************************/ int day_graph4(char *fname, char *title, u_long data1[24], u_long data2[24], u_long data3[24], unsigned long long data4[24]) { /* local variables */ int i, j, k, k2, l, m; int xbarleft; /* Leftmost point for a given bar to draw */ u_long maxval = 0; unsigned long long maxvol = 0; int width; /* width of the primary section */ int height; /* Height of the hits/pages/files section */ int bottom; int day_middle_x; /* px. Mid point offset between veritical bars */ int text_centring; /* px. Use to centre the Y Axis text against the grid lines */ int cur_text_start; /* px. start of current text. Used to print months on X Axis */ int section_left; /* Section break is here from left */ int section_right; /* End of Section from left, right postion */ int section_bottom; /* Bottom of vertical section line */ int bar_width; /* Width of the individual bars */ double bar_offset; /* basic offset of the individual bars */ int bar_gap = 3; /* px. Space between bar groupings */ int bar_sep = 2; /* px. Space between bars in a group */ int index_diff; /* Difference between start/end Use to set a maximum bar width */ int font_char_width; /* px. Width of an individual char in a given font - magic constant */ int font_char_height; /* px. Height of an individual char in a given font - magic constant */ int edge_text_offset = 0; /* px. Offset from the edge for side text */ char text_separator[2] = "/"; /* initalize the graph */ init_graph(title, graph_hourly_x, graph_hourly_y); /* Set up base calculations */ section_left = GRAPH_INNER_BOX_LEFT; section_right = graph_hourly_x - GRAPH_INNER_BOX_RIGHT - 2 - 1; /* 2 is width of inner box */ section_bottom = graph_hourly_y - GRAPH_INNER_BOX_BOTTOM; width = graph_hourly_x - GRAPH_INNER_BOX_LEFT - (GRAPH_INNER_BOX_RIGHT + 1) + 1; height = section_bottom - GRAPH_INNER_BOX_TOP - (GRAPH_TB_IN_OFFSET * 2) - 3; bottom = section_bottom - (GRAPH_TB_IN_OFFSET * 2); /* magic numbers */ font_char_width = 6; font_char_height = 10; if (graph_legend) { /* print color coded legends? */ /* Pages/Files/Hits Legend */ k = font_char_width / 2; /* Half Char Sep */ k2 = font_char_width * 2; /* Half Space '/' Half Space */ i = (strlen(_("Pages")) * font_char_width); j = (strlen(_("Files")) * font_char_width); l = (strlen(_("Hits")) * font_char_width); m = (strlen(_("Volume")) * font_char_width); edge_text_offset = graph_hourly_x - GRAPH_INNER_BOX_RIGHT; /* + GRAPH_TEXT_X_OFFSET; */ gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i - 1, (unsigned char *) _("Pages"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i, (unsigned char *) _("Pages"), PAGECOLOR); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + k2 - k - 1, (unsigned char *) text_separator, dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + k2 - k, (unsigned char *) text_separator, black); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + k2 - 1, (unsigned char *) _("Files"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + k2, (unsigned char *) _("Files"), FILECOLOR); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + k2 * 2 - k - 1, (unsigned char *) text_separator, dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + k2 * 2 - k, (unsigned char *) text_separator, black); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + l + k2 * 2 - 1, (unsigned char *) _("Hits"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + l + k2 * 2, (unsigned char *) _("Hits"), HITCOLOR); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + l + k2 * 3 - k - 1, (unsigned char *) text_separator, dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + l + k2 * 3 - k, (unsigned char *) text_separator, black); gdImageStringUp(im, gdFontSmall, edge_text_offset + 1, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + l + m + k2 * 3 - 1, (unsigned char *) _("Volume"), dkgrey); gdImageStringUp(im, gdFontSmall, edge_text_offset, GRAPH_INNER_BOX_TOP + GRAPH_TEXT_Y_OFFSET + i + j + l + m + k2 * 3, (unsigned char *) _("Volume"), KBYTECOLOR); } for (i = 0; i < 24; i++) { if (data1[i] > maxval) maxval = data1[i]; if (data2[i] > maxval) maxval = data2[i]; if (data3[i] > maxval) maxval = data3[i]; if (data4[i] > maxvol) maxvol = data4[i]; } if (maxval <= 0) maxval = 1; if (maxvol <= 0) maxvol = 1; /**************************************************** * Do the main Section * * Hits, Files, Pages, Volume * ****************************************************/ if (graph_lines) { edge_text_offset = 6 + GRAPH_TEXT_Y_OFFSET + 1; /* tiny font has height of 6 */ draw_horiz_grid(maxval, section_left + 1, section_right, bottom, height, edge_text_offset, 0); } /* width/offset calculations */ index_diff = 24; bar_width = calc_bar_width(width, index_diff, &bar_gap, &bar_sep, 3); bar_offset = (((double) width - ((double) bar_gap * 2.0)) / (double) index_diff); day_middle_x = (bar_width + bar_sep * 2) / 2 + 1; /* Calc for the mid point on which to then draw the month on the X Axis */ for (i = 0; i < 24; i++) { /* X Axis */ text_centring = (((double) strlen(numchar[i])) / 2.0) * (double) font_char_width; xbarleft = GRAPH_INNER_BOX_LEFT + GRAPH_LR_IN_OFFSET + bar_gap + (i * bar_offset); cur_text_start = xbarleft + day_middle_x - text_centring; gdImageString(im, gdFontSmall, cur_text_start, bottom + GRAPH_TEXT_Y_OFFSET, (unsigned char *) numchar[i], black); /* VOLUME */ percent = ((double) data4[i] / (double) maxvol); draw_bar(xbarleft, bottom, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, 0, KBYTECOLOR); /* HITS */ percent = ((double) data1[i] / (double) maxval); draw_bar(xbarleft + bar_sep, bottom, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, 0, HITCOLOR); /* FILES */ percent = ((double) data2[i] / (double) maxval); draw_bar(xbarleft + bar_sep + bar_sep, bottom, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, 0, FILECOLOR); /* PAGES */ percent = ((double) data3[i] / (double) maxval); draw_bar(xbarleft + bar_sep + bar_sep + bar_sep, bottom, GRAPH_INNER_BOX_TOP + 1, bar_width, percent, 0, PAGECOLOR); } /* save as png file */ if ((out = fopen(fname, "wb")) != NULL) { gdImagePng(im, out); fclose(out); } /* deallocate memory */ gdImageDestroy(im); return (0); } /***************************************************************** * * * PIE_CHART - draw a pie chart (10 data items max) * * * *****************************************************************/ int pie_chart(char *fname, char *title, unsigned long long t_val, unsigned long long data1[], char *legend[]) { int i, x, slice_percent, y; int centre_x; /* Centre for the X location */ int centre_y; /* Centre for the Y location */ int diamtr_x; /* diameter X Axis */ int diamtr_y; /* diameter Y Axis */ double XtoYratio = 5.0 / 6.0; /* Ratio of set X to Y dynamic => 5/6 = 0.833... */ int text_max_x; /* Max right point for text listing */ double s_arc = 0.0; int r, g, b; int colour_index; char buffer[128]; struct pie_data gdata; /* init graph and colors */ init_graph(title, graph_pie_x, graph_pie_y); /* First deallocate all the hit etc colours, and setup for using pie colours instead */ for (i = 4 + 6 - 1; i > 4 - 1; i--) { gdImageColorDeallocate(im, i); } r = getred(pie_color1); g = getgreen(pie_color1); b = getblue(pie_color1); colour_index = gdImageColorAllocate(im, r, g, b); r = getred(pie_color2); g = getgreen(pie_color2); b = getblue(pie_color2); colour_index = gdImageColorAllocate(im, r, g, b); r = getred(pie_color3); g = getgreen(pie_color3); b = getblue(pie_color3); colour_index = gdImageColorAllocate(im, r, g, b); r = getred(pie_color4); g = getgreen(pie_color4); b = getblue(pie_color4); colour_index = gdImageColorAllocate(im, r, g, b); /* Default extra colours */ colour_index = gdImageColorAllocate(im, 0, 192, 255); /* cyan */ colour_index = gdImageColorAllocate(im, 255, 255, 0); /* yellow */ colour_index = gdImageColorAllocate(im, 128, 0, 128); /* purple */ colour_index = gdImageColorAllocate(im, 128, 255, 192); /* ltgreen */ colour_index = gdImageColorAllocate(im, 255, 0, 255); /* ltpurple */ colour_index = gdImageColorAllocate(im, 255, 196, 128); /* brown */ /* base caclulations */ centre_x = (int) ((double) graph_pie_x * (1.0 / 3.0) + 1.0); centre_y = graph_pie_y / 2; diamtr_x = (centre_x - GRAPH_INNER_BOX_LEFT - GRAPH_PIE_X_INSET) * 2; diamtr_y = (int) ((double) diamtr_x * XtoYratio + 1.0); text_max_x = graph_pie_x - GRAPH_INNER_BOX_LEFT - GRAPH_PIE_X_INSET; VPRINT(VERBOSE5, "PIE: cx: %d cy: %d dx: %d dy: %d x: %d y: %d\n", centre_x, centre_y, diamtr_x, diamtr_y, graph_pie_x, graph_pie_y); /* do the circle... */ gdImageArc(im, centre_x, centre_y, diamtr_x, diamtr_y, 0, 360, black); gdImageArc(im, centre_x, centre_y + 10, diamtr_x - 2, diamtr_y - 2, 2, 178, black); gdImageFillToBorder(im, centre_x, centre_y + (diamtr_y / 2) + 1, black, black); /* slice the pie */ gdata = *calc_arc(0.0, 0.0, centre_x, centre_y, diamtr_x, diamtr_y); gdImageLine(im, centre_x, centre_y, gdata.x, gdata.y, black); /* inital line */ y = centre_y - (diamtr_y / 2); for (i = 0; i < 10; i++) { /* run through data array */ if ((data1[i] != 0) && (s_arc < 1.0)) { /* make sure valid slice */ slice_percent = (((double) data1[i] / t_val) + 0.005) * 100.0; if (slice_percent < 1) break; if (s_arc + ((double) slice_percent / 100.0) >= 1.0) { gdata = *calc_arc(s_arc, 1.0, centre_x, centre_y, diamtr_x, diamtr_y); s_arc = 1.0; } else { gdata = *calc_arc(s_arc, s_arc + ((double) slice_percent / 100.0), centre_x, centre_y, diamtr_x, diamtr_y); s_arc += (double) slice_percent / 100.0; } gdImageLine(im, centre_x, centre_y, gdata.x, gdata.y, black); gdImageFill(im, gdata.mx, gdata.my, i + 4); y += 20; } } /* Repeat drawing loop for text as longer text was stopping the fill from fully ... filling. */ y = centre_y - (diamtr_y / 2); s_arc = 0.0; for (i = 0; i < 10; i++) { /* run through data array */ if ((data1[i] != 0) && (s_arc < 1.0)) { /* make sure valid slice */ slice_percent = (((double) data1[i] / t_val) + 0.005) * 100.0; if (slice_percent < 1) break; if (s_arc + ((double) slice_percent / 100.0) >= 1.0) { s_arc = 1.0; } else { s_arc += (double) slice_percent / 100.0; } sprintf(buffer, "%s (%d%%)", legend[i], slice_percent); x = text_max_x - (strlen(buffer) * 7); gdImageString(im, gdFontMediumBold, x + 1, y + 1, (unsigned char *) buffer, black); gdImageString(im, gdFontMediumBold, x, y, (unsigned char *) buffer, i + 4); y += 20; } } if (s_arc < 1.0) { /* anything left over? */ gdata = *calc_arc(s_arc, 1.0, centre_x, centre_y, diamtr_x, diamtr_y); gdImageFill(im, gdata.mx, gdata.my, white); sprintf(buffer, "%s (%d%%)", _("Other"), 100 - (int) (s_arc * 100)); x = text_max_x - (strlen(buffer) * 7); gdImageString(im, gdFontMediumBold, x + 1, y + 1, (unsigned char *) buffer, black); gdImageString(im, gdFontMediumBold, x, y, (unsigned char *) buffer, white); } /* save png image */ if ((out = fopen(fname, "wb")) != NULL) { gdImagePng(im, out); fclose(out); } /* deallocate memory */ gdImageDestroy(im); return (0); } /***************************************************************** * * * CALC_ARC - generate x,y coordinates for pie chart * * * *****************************************************************/ struct pie_data * calc_arc(double min, double max, int cx, int cy, int dx, int dy) /* cx, cy - Centre X & Y * dx, dy - Diameter X & Y */ { static struct pie_data data; double d; /* Calculate max line */ d = max; data.x = cos(d * (2 * PI)) * ((dx - 2) / 2) + cx; data.y = sin(d * (2 * PI)) * ((dy - 2) / 2) + cy; /* Now get mid-point */ d = ((min + max) / 2); data.mx = cos(d * (2 * PI)) * (dx / 3) + cx; data.my = sin(d * (2 * PI)) * (dy / 3) + cy; return &data; } /**************************************************************** * * * INIT_GRAPH - initalize graph and draw borders * * 1 px black rectange on outer edge * * G_SHADOW_WIDTH px white/grey shadow effect * * 1 + 1 px black/white shadow'd line rectange - inner box * * inset G_INNER_BOX_TOP & G_INNER_BOX_LEFT * * white line is inset -1 again * * * ****************************************************************/ void init_graph(char *title, int xsize, int ysize) { int i, r, g, b; int edge_text_offset; /* px. Offset from the edge for descriptive text */ int font_char_height; /* px. Height of an individual char in a given font - magic constant */ im = gdImageCreate(xsize, ysize); /* allocate color maps, background color first (grey) */ grey = gdImageColorAllocate(im, 192, 192, 192); dkgrey = gdImageColorAllocate(im, 128, 128, 128); black = gdImageColorAllocate(im, 0, 0, 0); white = gdImageColorAllocate(im, 255, 255, 255); /* By default, we set up all graphs index'ed off hits, pages etc * which means for pie charts we need to deallocate these 6 colours */ r = getred(hit_color); g = getgreen(hit_color); b = getblue(hit_color); hitcolor = gdImageColorAllocate(im, r, g, b); r = getred(site_color); g = getgreen(site_color); b = getblue(site_color); sitecolor = gdImageColorAllocate(im, r, g, b); r = getred(file_color); g = getgreen(file_color); b = getblue(file_color); filecolor = gdImageColorAllocate(im, r, g, b); r = getred(kbyte_color); g = getgreen(kbyte_color); b = getblue(kbyte_color); kbytecolor = gdImageColorAllocate(im, r, g, b); r = getred(page_color); g = getgreen(page_color); b = getblue(page_color); pagecolor = gdImageColorAllocate(im, r, g, b); r = getred(visit_color); g = getgreen(visit_color); b = getblue(visit_color); visitcolor = gdImageColorAllocate(im, r, g, b); /* make borders */ for (i = 1; i < (GRAPH_SHADOW_WIDTH + 1); i++) { /* do shadow effect */ gdImageLine(im, i, i, xsize - (i + 1), i, white); gdImageLine(im, i, i, i, ysize - (i + 1), white); gdImageLine(im, i + 1, ysize - (i + 1), xsize - (i + 1), ysize - (i + 1), dkgrey); gdImageLine(im, xsize - (i + 1), i + 1, xsize - (i + 1), ysize - (i + 1), dkgrey); } gdImageRectangle(im, GRAPH_INNER_BOX_LEFT, GRAPH_INNER_BOX_TOP, xsize - (GRAPH_INNER_BOX_LEFT + 1), ysize - (GRAPH_INNER_BOX_BOTTOM + 1), black); gdImageRectangle(im, (GRAPH_INNER_BOX_LEFT - 1), (GRAPH_INNER_BOX_TOP - 1), xsize - (GRAPH_INNER_BOX_LEFT + 2), ysize - (GRAPH_INNER_BOX_BOTTOM + 2), white); gdImageRectangle(im, 0, 0, xsize - 1, ysize - 1, black); /* display the graph title */ font_char_height = 10; edge_text_offset = GRAPH_INNER_BOX_TOP - font_char_height - GRAPH_TEXT_Y_OFFSET - 1; gdImageString(im, gdFontMediumBold, GRAPH_INNER_BOX_LEFT, edge_text_offset, (unsigned char *) title, filecolor); return; }