plotting data coming from a live database using mysql in a webpage using php/html.

thanks in advacne.^^

Recommended Answers

All 4 Replies

Could you please be more specific. How much data are you talking about, what kind of data, and what type of graph should be plotted?
What have you written (please post your code here) and what is going wrong?

we are talking about precised data here..lets say the maximum value for y = 3.4025 and lowest is 3.4000..data varies around taht range only. i already have the code but it does not work for minute amount.

So, all you need is to make some changes in the code to get it working. And we can always help, but .... where is the code? Or is 6000 lines long?

What does the code do if you multiply your data with 10 or 100 so your lowest value will be 34 or 34000 and the highest 34099? If your code can handle results like that, the problem would be solved.

here is the code sir..

im using PHPlot in plotting graphs, here is the code which makes the graph..

i deleted the fonts, colors and ticks code..if ever you needed the full code i can send it thru mail. thanks, thanks, i will really appreciate
if you could help me debug this one. the data we are talking ranges from e.g. (5.7041 - 5.7009)

thanks thansk.

phplot.php

<?php

if (! defined(__FUNCTION__))
    define(__FUNCTION__, '__FUNCTION__ Requires at least PHP 4.3.0.');

define ('MINY', -1);        // Indexes in $data (for DrawXDataLine())
define ('MAXY', -2);
define ('TOTY', -3);

error_reporting(E_ALL);

class PHPlot 
{
    //////////////// CONFIG PARAMETERS //////////////////////

    var $is_inline = FALSE;             // FALSE = Sends headers, TRUE = sends just raw image data
    var $browser_cache = FALSE;         // FALSE = Sends headers for browser to not cache the image,
                                        // (only if is_inline = FALSE also)

    var $safe_margin = 5;               // Extra margin used in several places. In pixels

    var $x_axis_position = '';          // Where to draw both axis (world coordinates),
    var $y_axis_position = '';          // leave blank for X axis at 0 and Y axis at left of plot.

    var $xscale_type = 'linear';        // linear, log
    var $yscale_type = 'linear';

//Fonts
    var $use_ttf  = FALSE;                  // Use True Type Fonts?
    var $ttf_path = '.';                    // Default path to look in for TT Fonts.
    var $default_ttfont = 'benjamingothic.ttf';
    var $line_spacing = 4;                  // Pixels between lines.

// Font angles: 0 or 90 degrees for fixed fonts, any for TTF
    var $x_label_angle = 0;                 // For labels on X axis (tick and data)
    var $y_label_angle = 0;                 // For labels on Y axis (tick and data)
    var $x_title_angle = 0;                 // Don't change this if you don't want to screw things up!
    var $y_title_angle = 90;                // Nor this.
    var $title_angle = 0;                   // Or this.

//Formats
    var $file_format = 'png';
    var $output_file = '';                  // For output to a file instead of stdout

//Data
    var $data_type = 'text-data';           // text-data, data-data-error, data-data, text-data-single
    var $plot_type= 'linepoints';           // bars, lines, linepoints, area, points, pie, thinbarline, squared

    var $label_scale_position = 0.5;        // Shifts data labes in pie charts. 1 = top, 0 = bottom
    var $group_frac_width = 0.7;            // value from 0 to 1 = width of bar groups
    var $bar_width_adjust = 1;              // 1 = bars of normal width, must be > 0

    var $y_precision = 1;
    var $x_precision = 1;

    var $data_units_text = '';              // Units text for 'data' labels (i.e: 'ยค', '$', etc.)

// Titles
    var $title_txt = 'X-Bar R Chart';

    var $x_title_txt = '';
    var $x_title_pos = 'plotdown';          // plotdown, plotup, both, none

    var $y_title_txt = '';
    var $y_title_pos = 'plotleft';          // plotleft, plotright, both, none


//Labels
    // There are two types of labels in PHPlot:
    //    Tick labels: they follow the grid, next to ticks in axis.   (DONE)
    //                 they are drawn at grid drawing time, by DrawXTicks() and DrawYTicks()
    //    Data labels: they follow the data points, and can be placed on the axis or the plot (x/y)  (TODO)
    //                 they are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc.
    //                 Draw*DataLabel() also draws H/V lines to datapoints depending on draw_*_data_label_lines

    // Tick Labels
    var $x_tick_label_pos = 'plotdown';     // plotdown, plotup, both, xaxis, none
    var $y_tick_label_pos = 'plotleft';     // plotleft, plotright, both, yaxis, none

    // Data Labels:
    var $x_data_label_pos = 'plotdown';     // plotdown, plotup, both, plot, all, none
    var $y_data_label_pos = 'plotleft';     // plotleft, plotright, both, plot, all, none

    var $draw_x_data_label_lines = FALSE;   // Draw a line from the data point to the axis?
    var $draw_y_data_label_lines = FALSE;   // TODO

    // Label types: (for tick, data and plot labels)
    var $x_label_type = '';                 // data, time. Leave blank for no formatting.
    var $y_label_type = '';                 // data, time. Leave blank for no formatting.
    var $x_time_format = '%H:%m:%s';        // See http://www.php.net/manual/html/function.strftime.html
    var $y_time_format = '%H:%m:%s';        // SetYTimeFormat() too...

    // Skipping labels
    var $x_label_inc = 1;                   // Draw a label every this many (1 = all) (TODO)
    var $y_label_inc = 1;
    var $_x_label_cnt = 0;                  // internal count FIXME: work in progress

    // Legend
    var $legend = '';                       // An array with legend titles
    var $legend_x_pos = '';
    var $legend_y_pos = '';


//Ticks
    var $x_tick_length = 5;                 // tick length in pixels for upper/lower axis
    var $y_tick_length = 5;                 // tick length in pixels for left/right axis

    var $x_tick_cross = 3;                  // ticks cross x axis this many pixels
    var $y_tick_cross = 3;                  // ticks cross y axis this many pixels

    var $x_tick_pos = 'plotdown';           // plotdown, plotup, both, xaxis, none
    var $y_tick_pos = 'plotleft';           // plotright, plotleft, both, yaxis, none

    var $num_x_ticks = '';
    var $num_y_ticks = '';

    var $x_tick_inc = '';                   // Set num_x_ticks or x_tick_inc, not both.
    var $y_tick_inc = '';                   // Set num_y_ticks or y_tick_inc, not both.

    var $skip_top_tick = FALSE;
    var $skip_bottom_tick = FALSE;
    var $skip_left_tick = FALSE;
    var $skip_right_tick = FALSE;

//Grid Formatting
    var $draw_x_grid = FALSE;
    var $draw_y_grid = TRUE;

    var $dashed_grid = TRUE;
    var $grid_at_foreground = FALSE;        // Chooses whether to draw the grid below or above the graph

//Colors and styles       (all colors can be array (R,G,B) or named color)
    var $color_array = 'small';             // 'small', 'large' or array (define your own colors)
                                            // See rgb.inc.php and SetRGBArray()
    var $i_border = array(194, 194, 194);
    var $plot_bg_color = 'white';
    var $bg_color = 'white';
    var $label_color = 'black';
    var $text_color = 'black';
    var $grid_color = 'black';
    var $light_grid_color = 'gray';
    var $tick_color = 'black';
    var $title_color = 'black';
    var $data_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
    var $error_bar_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
    var $data_border_colors = array('black');

    var $line_widths = 1;                  // single value or array
    var $line_styles = array('solid', 'solid', 'dashed');   // single value or array
    var $dashed_style = '2-4';              // colored dots-transparent dots

    var $point_sizes = array(5,5,3);         // single value or array
    var $point_shapes = array('dot');   // rect, circle, diamond, triangle, dot, line, halfline, cross

    var $error_bar_size = 5;                // right and left size of tee
    var $error_bar_shape = 'tee';           // 'tee' or 'line'
    var $error_bar_line_width = 1;          // single value (or array TODO)

    var $plot_border_type = 'sides';        // left, sides, none, full
    var $image_border_type = 'none';        // 'raised', 'plain', 'none'

    var $shading = 5;                       // 0 for no shading, > 0 is size of shadows in pixels

    var $draw_plot_area_background = FALSE;
    var $draw_broken_lines = FALSE;          // Tells not to draw lines for missing Y data.


//////////////////////////////////////////////////////
//BEGIN CODE
//////////////////////////////////////////////////////

    /*!
     * Constructor: Setup img resource, colors and size of the image, and font sizes.
     *
     * \param which_width       int    Image width in pixels.
     * \param which_height      int    Image height in pixels.
     * \param which_output_file string Filename for output.
     * \param which_input_fule  string Path to a file to be used as background.
     */
    function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL)
    {
        /*
         * Please see http://www.php.net/register_shutdown_function
         * PLEASE NOTE: register_shutdown_function() will take a copy of the object rather than a reference
         * so we put an ampersand. However, the function registered will work on the object as it
         * was upon registration. To solve this, one of two methods can be used:
         *      $obj = new object();
         *      register_shutdown_function(array(&$obj,'shutdown'));
         * OR
         *      $obj = &new object();
         * HOWEVER, as the second statement assigns $obj a reference to the current object, it might be that
         * several instances mess things up... (CHECK THIS)
         *
         * AND
         *    as $this->img is set upon construction of the object, problems will not arise for us (for the
         *    moment maybe, so I put all this here just in case)
         */
        register_shutdown_function(array(&$this, '_PHPlot'));

        $this->SetRGBArray($this->color_array);

        $this->background_done = FALSE;     // Set to TRUE after background image is drawn once

        if ($which_output_file)
            $this->SetOutputFile($which_output_file);

        if ($which_input_file)
            $this->SetInputFile($which_input_file);
        else 
		{
            $this->image_width = $which_width;
            $this->image_height = $which_height;

            $this->img = ImageCreate($this->image_width, $this->image_height);
            if (! $this->img)
                $this->PrintError('PHPlot(): Could not create image resource.');
		}

        $this->SetDefaultStyles();
        $this->SetDefaultFonts();

        $this->SetTitle('');
        $this->SetXTitle('');
        $this->SetYTitle('');

        $this->print_image = TRUE;      // Use for multiple plots per image (TODO: automatic)
    }

    /*!
     * Destructor. Image resources not deallocated can be memory hogs, I think
     * it is safer to automatically call imagedestroy upon script termination than
     * do it ourselves.
     * See notes in the constructor code.
     */
    function _PHPlot ()
    {
        ImageDestroy($this->img);
        return;
    }


/////////////////////////////////////////////
///////////            INPUT / OUTPUT CONTROL
/////////////////////////////////////////////

    /*!
     * Sets output file format.
     */
    function SetFileFormat($format)
    {
        $asked = $this->CheckOption($format, 'jpg, png, gif, wbmp', __FUNCTION__);

        switch ($asked) 
		{
			case 'jpg':
				if (imagetypes() & IMG_JPG)
					$this->file_format = 'jpg';
					return TRUE;
				break;
			case 'png':
				if (imagetypes() & IMG_PNG)
					$this->file_format = 'png';
					return TRUE;
				break;
			case 'gif':
				if (imagetypes() & IMG_GIF)
					$this->file_format = 'gif';
					return TRUE;
				break;
			case 'wbmp':
				if (imagetypes() & IMG_WBMP)
					$this->file_format = 'wbmp';
					return TRUE;
				break;
			default:
				$this->PrintError("SetFileFormat():File format '$format' not supported");
				return FALSE;
        }
    }


    /*!
     * Selects an input file to be used as graph background and scales or tiles this image
     * to fit the sizes.
     *  \param input_file string Path to the file to be used (jpeg, png and gif accepted)
     *  \param mode       string 'centeredtile', 'tile', 'scale' (the image to the graph's size)
     */
    function SetBgImage($input_file, $mode='centeredtile')
    {
        $this->bgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
        $this->bgimg  = $input_file;
    }

    /*!
     * Selects an input file to be used as plot area background and scales or tiles this image
     * to fit the sizes.
     *  \param input_file string Path to the file to be used (jpeg, png and gif accepted)
     *  \param mode       string 'centeredtile', 'tile', 'scale' (the image to the graph's size)
     */
    function SetPlotAreaBgImage($input_file, $mode='tile')
    {
        $this->plotbgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
        $this->plotbgimg  = $input_file;
    }


    /*!
     * Sets the name of the file to be used as output file.
     */
    function SetOutputFile($which_output_file)
    {
        $this->output_file = $which_output_file;
        return TRUE;
    }

    /*!
     * Sets the output image as 'inline', that is: no Content-Type headers are sent
     * to the browser. Needed if you want to embed the images.
     */
    function SetIsInline($which_ii)
    {
        $this->is_inline = (bool)$which_ii;
        return TRUE;
    }


    /*!
     * Performs the actual outputting of the generated graph, and
     * destroys the image resource.
     */
    function PrintImage()
    {
        // Browser cache stuff submitted by Thiemo Nagel
        if ( (! $this->browser_cache) && (! $this->is_inline)) 
		{
            header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
            header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
            header('Cache-Control: no-cache, must-revalidate');
            header('Pragma: no-cache');
        }

        switch($this->file_format) 
		{
			case 'png':
				if (! $this->is_inline) 
				{
					Header('Content-type: image/png');
				}
				if ($this->is_inline && $this->output_file != '') 
				{
					ImagePng($this->img, $this->output_file);
				} 
				else 
				{
					ImagePng($this->img);
				}
				break;
			case 'jpg':
				if (! $this->is_inline) 
				{
					Header('Content-type: image/jpeg');
				}
				if ($this->is_inline && $this->output_file != '') 
				{
					ImageJPEG($this->img, $this->output_file);
				} 
				else 
				{
					ImageJPEG($this->img);
				}
				break;
			case 'gif':
				if (! $this->is_inline) 
				{
					Header('Content-type: image/gif');
				}
				if ($this->is_inline && $this->output_file != '') 
				{
					ImageGIF($this->img, $this->output_file);
				} 
				else 
				{
					ImageGIF($this->img);
				}
	
				break;
			case 'wbmp':        // wireless bitmap, 2 bit.
				if (! $this->is_inline) 
				{
					Header('Content-type: image/wbmp');
				}
				if ($this->is_inline && $this->output_file != '') 
				{
					ImageWBMP($this->img, $this->output_file);
				} 
				else 
				{
					ImageWBMP($this->img);
				}
				break;
			default:
				$this->PrintError('PrintImage(): Please select an image type!');
				break;
			}
        return TRUE;
    }

    /*! 
     * Prints an error message to stdout and dies 
     */
    function PrintError($error_message) 
    {
        echo "<p><b>Fatal error</b>: $error_message<p>";
        die;
    }

    /*!
     * Prints an error message inline into the generated image and draws it centered
     * around the given coordinates (defaults to center of the image)
     *   \param error_message Message to be drawn
     *   \param where_x       X coordinate
     *   \param where_y       Y coordinate
     */
    function DrawError($error_message, $where_x = NULL, $where_y = NULL) 
    {
        if (! $this->img)
            $this->PrintError('_DrawError(): Warning, no image resource allocated. '. 'The message to be written was: '.$error_message);

        $ypos = (! $where_y) ? $this->image_height/2 : $where_y;
        $xpos = (! $where_x) ? $this->image_width/2 : $where_x;
        ImageRectangle($this->img, 0, 0, $this->image_width, $this->image_height, ImageColorAllocate($this->img, 255, 255, 255));

        $this->DrawText($this->generic_font, 0, $xpos, $ypos, ImageColorAllocate($this->img, 0, 0, 0), $error_message, 'center', 'center');

        $this->PrintImage();
        exit;
//        return TRUE;
    }


/////////////////////////////////////////////
///////////                              MISC
/////////////////////////////////////////////

    /*!
     * Checks the valididy of an option.
     *  \param which_opt  String to check.
     *  \param which_acc  String of accepted choices.
     *  \param which_func Name of the calling function, for error messages.
     *  \note If checking everywhere for correctness slows things down, we could provide a
     *        child class overriding every Set...() method which uses CheckOption(). Those new
     *        methods could proceed in the unsafe but faster way.
     */
    function CheckOption($which_opt, $which_acc, $which_func)
    {
        $asked = trim($which_opt);

        // FIXME: this for backward compatibility, as eregi() fails with empty strings.
        if ($asked == '')
            return '';

        $asked = strtolower($asked);
        if (@ eregi($asked, $which_acc)) 
		{
            return $asked;
        } 
		else 
		{
            $this->DrawError("$which_func(): '$which_opt' not in available choices: '$which_acc'.");
            return NULL;
        }
    }


    /*!
     *  \note Submitted by Thiemo Nagel
     */
    function SetBrowserCache($which_browser_cache)
    {
        $this->browser_cache = $which_browser_cache;
        return TRUE;
    }

    /*!
     * Whether to show the final image or not
     */
    function SetPrintImage($which_pi)
    {
        $this->print_image = $which_pi;
        return TRUE;
    }

    /*!
     * Sets the graph's legend. If argument is not an array, appends it to the legend.
     */
    function SetLegend($which_leg)
    {
        if (is_array($which_leg)) 
		{             // use array
            $this->legend = $which_leg;
            return TRUE;
        } 
		else if (! is_null($which_leg)) 
		{     // append string
            $this->legend[] = $which_leg;
            return TRUE;
        } 
		else 
		{
            $this->DrawError("SetLegend(): argument must not be null.");
            return FALSE;
        }
    }

    /*!
     * Specifies the absolute (relative to image's up/left corner) position
     * of the legend's upper/leftmost corner.
     *  $which_type not yet used (TODO)
     */
    function SetLegendPixels($which_x, $which_y, $which_type=NULL) 
    { 
        $this->legend_x_pos = $which_x;
        $this->legend_y_pos = $which_y;

        return TRUE;
    }

    /*!
     * Specifies the relative (to graph's origin) position of the legend's
     * upper/leftmost corner. MUST be called after scales are set up.
     *   $which_type not yet used (TODO)
     */
    function SetLegendWorld($which_x, $which_y, $which_type=NULL) 
    { 
        if (! isset($this->scale_is_set))
            $this->CalcTranslation();

        $this->legend_x_pos = $this->xtr($which_x);
        $this->legend_y_pos = $this->ytr($which_y);

        return TRUE;
    }

    /*!
     * Accepted values are: left, sides, none, full
     */
    function SetPlotBorderType($pbt)
    {
        $this->plot_border_type = $this->CheckOption($pbt, 'left, sides, none, full', __FUNCTION__);
    }

    /*!
     * Accepted values are: raised, plain
     */
    function SetImageBorderType($sibt) 
    {
        $this->image_border_type = $this->CheckOption($sibt, 'raised, plain', __FUNCTION__);
    }


    /*!
     * \param dpab bool
     */
    function SetDrawPlotAreaBackground($dpab)
    {
        $this->draw_plot_area_background = (bool)$dpab;
    }


    /*!
     * \param dyg bool 
     */
    function SetDrawYGrid($dyg) 
    {
        $this->draw_y_grid = (bool)$dyg;
        return TRUE;
    }


    /*!
     * \param dxg bool
     */
    function SetDrawXGrid($dxg) 
    {
        $this->draw_x_grid = (bool)$dxg;
        return TRUE;
    }


    /*!
     * \param ddg bool 
     */
    function SetDrawDashedGrid($ddg) 
    {
        $this->dashed_grid = (bool)$ddg;
        return TRUE;
    }


    /*!
     * \param dxdl bool
     */
    function SetDrawXDataLabelLines($dxdl)
    {
        $this->draw_x_data_label_lines = (bool)$dxdl;
        return TRUE;

    }

    
    /*!
     * TODO: draw_y_data_label_lines not implemented.
     * \param dydl bool
     */
    function SetDrawYDataLabelLines($dydl)
    {
        $this->draw_y_data_label_lines = $dydl;
        return TRUE;
    }
    
    /*!
     * Sets the graph's title.
     * TODO: add parameter to choose title placement: left, right, centered=
     */
    function SetTitle($which_title) 
    {
        $this->title_txt = $which_title;

        if ($which_title == '') 
		{
            $this->title_height = 0;
            return TRUE;
        }            

        $str = split("\n", $which_title);
        $lines = count($str);
        $spacing = $this->line_spacing * ($lines - 1);

        if ($this->use_ttf) 
		{
            $size = $this->TTFBBoxSize($this->title_font['size'], 0, $this->title_font['font'], $which_title);
            $this->title_height = $size[1] * $lines;
        } 
		else 
		{
            $this->title_height = ($this->title_font['height'] + $spacing) * $lines;
        }   
        return TRUE;
    }

    /*!
     * Sets the X axis title and position.
     */
    function SetXTitle($which_xtitle, $which_xpos = 'plotdown') 
    {
        if ($which_xtitle == '')
            $which_xpos = 'none';

        $this->x_title_pos = $this->CheckOption($which_xpos, 'plotdown, plotup, both, none', __FUNCTION__);

        $this->x_title_txt = $which_xtitle;

        $str = split("\n", $which_xtitle);
        $lines = count($str);
        $spacing = $this->line_spacing * ($lines - 1);

        if ($this->use_ttf) 
		{
            $size = $this->TTFBBoxSize($this->x_title_font['size'], 0, $this->x_title_font['font'], $which_xtitle);
            $this->x_title_height = $size[1] * $lines;
        } 
		else 
		{
            $this->x_title_height = ($this->y_title_font['height'] + $spacing) * $lines;
        }

        return TRUE;
    }


    /*!
     * Sets the Y axis title and position.
     */
    function SetYTitle($which_ytitle, $which_ypos = 'plotleft') 
    {
        if ($which_ytitle == '')
            $which_ypos = 'none';

        $this->y_title_pos = $this->CheckOption($which_ypos, 'plotleft, plotright, both, none', __FUNCTION__);

        $this->y_title_txt = $which_ytitle;

        $str = split("\n", $which_ytitle);
        $lines = count($str);
        $spacing = $this->line_spacing * ($lines - 1);

        if ($this->use_ttf) 
		{
            $size = $this->TTFBBoxSize($this->y_title_font['size'], 90, $this->y_title_font['font'],  $which_ytitle);
            $this->y_title_width = $size[0] * $lines;
        } 
		else 
		{
            $this->y_title_width = ($this->y_title_font['height'] + $spacing) * $lines;
        }

        return TRUE;
    }

   /*!
     * Sets the position of Y axis.
     * \param pos int Position in world coordinates.
     */
    function SetYAxisPosition($pos)
    {
        $this->y_axis_position = (int)$pos;
        if (isset($this->scale_is_set)) 
		{
            $this->CalcTranslation();
        }
        return TRUE;
    }
    
    /*!
     * Sets the position of X axis.
     * \param pos int Position in world coordinates. 
     */
    function SetXAxisPosition($pos)
    {
        $this->x_axis_position = (int)$pos;
        if (isset($this->scale_is_set)) 
		{
            $this->CalcTranslation();
        }
        return TRUE;
    }

    function SetXScaleType($which_xst)
    {
        $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__);
        return TRUE;
    }
	/*
    function SetYScaleType($which_yst)
    {
        $this->yscale_type = $this->CheckOption($which_yst, 'linear, log',  __FUNCTION__);
        return TRUE;
    }
	
    function SetPrecisionX($which_prec)
    {
        $this->x_precision = $which_prec;
        $this->SetXLabelType('data');
        return TRUE;
    }
	*/
    function SetPrecisionY($which_prec)		
    {
        $this->y_precision = $which_prec;
        $this->SetYLabelType('data');
        return TRUE;
    }
	

    function SetErrorBarLineWidth($which_seblw)
    {
        $this->error_bar_line_width = $which_seblw;
        return TRUE;
    }

    function SetLabelScalePosition($which_blp)
    {
        //0 to 1
        $this->label_scale_position = $which_blp;
        return TRUE;
    }

    function SetErrorBarSize($which_ebs)
    {
        //in pixels
        $this->error_bar_size = $which_ebs;
        return TRUE;
    }

    /*!
     * Can be one of: 'tee', 'line'
     */
    function SetErrorBarShape($which_ebs)
    {
        $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__);
    }

    /*!
     * Sets point shape for each data set via an array.
     * Shape can be one of: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
     * 'diamond', 'triangle', 'trianglemid'
     */
    function SetPointShapes($which_pt)
    {
        if (is_null($which_pt)) 
		{
            // Do nothing, use default value.
        } 
		else if (is_array($which_pt)) 
		{
            // Did we get an array with point shapes?
            $this->point_shapes = $which_pt;
        } 
		else 
		{
            // Single value into array
            $this->point_shapes = array($which_pt);
        }

        foreach ($this->point_shapes as $shape)
        {
            // TODO, better check, per element rectification
            $this->CheckOption($shape, 'halfline, line, plus, cross, rect, circle, dot, diamond, triangle, trianglemid', __FUNCTION__);
        }

        // Make both point_shapes and point_sizes same size.
        $ps = count($this->point_sizes);
        $pt = count($this->point_shapes);

        if ($ps < $pt) 
		{
            array_pad_array($this->point_sizes, $pt);
        } 
		else if ($pt > $ps) 
		{
            array_pad_array($this->point_shapes, $ps);
        }
        return TRUE;
    }

    /*!
     * Sets the point size for point plots.
     * \param ps int Size in pixels.
     * \note Test this more extensively
     */
    function SetPointSizes($which_ps)
    {
        if (is_null($which_ps)) 
		{
            // Do nothing, use default value.
        } 
		else if (is_array($which_ps)) 
		{
            // Did we get an array with point sizes?
            $this->point_sizes = $which_ps;
        }
		else 
		{
            // Single value into array
            $this->point_sizes = array($which_ps);
        }

        // Make both point_shapes and point_sizes same size.
        $ps = count($this->point_sizes);
        $pt = count($this->point_shapes);

        if ($ps < $pt) 
		{
            array_pad_array($this->point_sizes, $pt);
        } 
		else if ($pt > $ps) 
		{
            array_pad_array($this->point_shapes, $ps);
        }

        // Fix odd point sizes for point shapes which need it
        for ($i = 0; $i < $pt; $i++) 
		{
            if ($this->point_shapes[$i] == 'diamond' or $this->point_shapes[$i] == 'triangle') 
			{
                if ($this->point_sizes[$i] % 2 != 0) 
				{
                    $this->point_sizes[$i]++;
                }
            }
        }
        return TRUE;
    }


    /*!
     * Tells not to draw lines for missing Y data. Only works with 'lines' and 'squared' plots.
     * \param bl bool
     */
    function SetDrawBrokenLines($bl)
    {
        $this->draw_broken_lines = (bool)$bl;
    }


    /*!
     *  text-data: ('label', y1, y2, y3, ...)
     *  text-data-single: ('label', data), for some pie charts.
     *  data-data: ('label', x, y1, y2, y3, ...)
     *  data-data-error: ('label', x1, y1, e1+, e2-, y2, e2+, e2-, y3, e3+, e3-, ...)
     */
    function SetDataType($which_dt)
    {
        //The next four lines are for past compatibility.
        if ($which_dt == 'text-linear') { $which_dt = 'text-data'; };
        if ($which_dt == 'linear-linear') { $which_dt = 'data-data'; };
        if ($which_dt == 'linear-linear-error') { $which_dt = 'data-data-error'; };
        if ($which_dt == 'text-data-pie') { $which_dt = 'text-data-single'; }

        $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-single, '.'data-data, data-data-error', __FUNCTION__);
        return TRUE;
    }
																			
    /*!
     * Copy the array passed as data values. We convert to numerical indexes, for its
     * use for (or while) loops, which sometimes are faster. Performance improvements		***********************************************************************************
     * vary from 28% in DrawLines() to 49% in DrawArea() for plot drawing functions.
     */
    function SetDataValues(&$which_dv)
    {
        unset ($this->data_limits_done);        // Reset this for every new data_set
        $this->num_data_rows = count($which_dv);
        $this->total_records = 0;               // Perform some useful calculations.
        $this->records_per_group = 1;           
        for ($i = 0, $recs = 0; $i < $this->num_data_rows; $i++) 
		{
            // Copy
            $this->data[$i] = array_values($which_dv[$i]);   // convert to numerical indices.

            // Compute some values
            $recs = count($this->data[$i]); 
            $this->total_records += $recs;

            if ($recs > $this->records_per_group)
                $this->records_per_group = $recs;

            $this->num_recs[$i] = $recs;
        }
    }

    /*!
     * Pad styles arrays for later use by plot drawing functions:
     * This removes the need for $max_data_colors, etc. and $color_index = $color_index % $max_data_colors
     * in DrawBars(), DrawLines(), etc.
     */
    function PadArrays()
    {
        array_pad_array($this->line_widths, $this->records_per_group);
        array_pad_array($this->line_styles, $this->records_per_group);

        array_pad_array($this->data_colors, $this->records_per_group);
        array_pad_array($this->data_border_colors, $this->records_per_group);
        array_pad_array($this->error_bar_colors, $this->records_per_group);

        $this->SetDataColors();
        $this->SetDataBorderColors();
        $this->SetErrorBarColors();

        return TRUE;
    }


//////////////////////////////////////////////////////////
///////////         DATA ANALYSIS, SCALING AND TRANSLATION
//////////////////////////////////////////////////////////

    /*!
     * Analizes data and sets up internal maxima and minima
     * Needed by: CalcMargins(), ...
     *   Text-Data is different than data-data graphs. For them what
     *   we have, instead of X values, is # of records equally spaced on data.
     *   text-data is passed in as $data[] = (title, y1, y2, y3, y4, ...)
     *   data-data is passed in as $data[] = (title, x, y1, y2, y3, y4, ...)
     */
	 
    function FindDataLimits()
    {
        // Set some default min and max values before running through the data
        switch ($this->data_type) 
		{
        	case 'text-data':		$minx = 0;				
									$maxx = 23;
									$miny = 2.4896;
									$maxy = 2.4904;
									break;
	        default:            	break;
        }
        $minminy = $miny;
        $maxmaxy = $maxy;

        if ($this->plot_type == 'stackedbars') 
		{
			$maxmaxy = $minminy = 0; 
		}

        // Process each row of data
        for ($i=0; $i < $this->num_data_rows; $i++) 
		{
            $j=0;
            // Extract maximum text label length
            $val = @ strlen($this->data[$i][$j++]);
            $maxt = ($val > $maxt) ? $val : $maxt;

            if ($this->plot_type == 'stackedbars') 
			{ 
				$maxy = $miny = 0; 
			}

            switch ($this->data_type) 
			{
				case 'text-data':           // Data is passed in as (title, y1, y2, y3, ...)
				case 'text-data-single':    // This one is for some pie charts
					// $numrecs = @ count($this->data[$i]);
					$miny = $maxy = (double)$this->data[$i][$j];
					for (; $j < $this->num_recs[$i]; $j++) 
					{
						$val = (double)$this->data[$i][$j];
						if ($this->plot_type == 'stackedbars') 
						{
							$maxy += abs($val);      // only positive values for the moment
						} 
						else 
						{
							$maxy = ($val > $maxy) ? $val : $maxy;
							$miny = ($val < $miny) ? $val : $miny;
						}
					}
					break;
				case 'data-data':           // Data is passed in as (title, x, y, y2, y3, ...)
					// X value:
					$val = (double)$this->data[$i][$j++];
					$maxx = ($val > $maxx) ? $val : $maxx;
					$minx = ($val < $minx) ? $val : $minx;
	
					$miny = $maxy = (double)$this->data[$i][$j];
					// $numrecs = @ count($this->data[$i]);
					for (; $j < $this->num_recs[$i]; $j++) 
					{
						$val = (double)$this->data[$i][$j];
						$maxy = ($val > $maxy) ? $val : $maxy;
						$miny = ($val < $miny) ? $val : $miny;
					}
					break;
				case 'data-data-error':     // Data is passed in as (title, x, y, err+, err-, y2, err2+, err2-,...)
					// X value:
					$val = (double)$this->data[$i][$j++];
					$maxx = ($val > $maxx) ? $val : $maxx;
					$minx = ($val < $minx) ? $val : $minx;
	
					$miny = $maxy = (double)$this->data[$i][$j];
					// $numrecs = @ count($this->data[$i]);
					for (; $j < $this->num_recs[$i];) 
					{
						// Y value:
						$val = (double)$this->data[$i][$j++];
						$maxy = ($val > $maxy) ? $val : $maxy;
						$miny = ($val < $miny) ? $val : $miny;
						// Error +:
						$val = (double)$this->data[$i][$j++];
						$maxe = ($val > $maxe) ? $val : $maxe;
						// Error -:
						$val = (double)$this->data[$i][$j++];
						$mine = ($val > $mine) ? $val : $mine;
					}
					$maxy = $maxy + $maxe;
					$miny = $miny - $mine;      // assume error bars are always > 0
					break;
				default:
					$this->PrintError("FindDataLimits(): Unknown data type '$data_type'.");
				break;
            }
            $this->data[$i][MINY] = $miny;      // This row's min Y, for DrawXDataLine()
            $this->data[$i][MAXY] = $maxy;      // This row's max Y, for DrawXDataLine()

            $minminy = ($miny < $minminy) ? $miny : $minminy;   // global min
            $maxmaxy = ($maxy > $maxmaxy) ? $maxy : $maxmaxy;   // global max
        }

        $this->min_x = $minx;
        $this->max_x = $maxx;
        $this->min_y = $minminy;
        $this->max_y = $maxmaxy;
        $this->max_t = $maxt;

        $this->data_limits_done = TRUE;

        return TRUE;
    }


    /*!
     * Calculates image margins on the fly from title positions and sizes,
     * and tick labels positions and sizes.
     *
     * FIXME: fix x_data_label_pos behaviour. Now we are leaving room for it AND x_tick_label_pos
     *        maybe it shouldn't be so...
     *
     * FIXME: y_data_label_pos is not yet used...
     *
     * TODO: add x_tick_label_width and y_tick_label_height and use them to calculate
     *       max_x_labels and max_y_labels, to be used by drawing functions to avoid overlapping.
     */
    function CalcMargins()
    {
        // Temporary variables for label size calculation
        $xlab = $this->FormatLabel('x', $this->max_x);
        $ylab = $this->FormatLabel('y', $this->max_y);

        // dirty fix:
        // max_t is the maximum data label length (from column 0 of each data row).
        if ($this->max_t > strlen ($xlab))
            $xlab = sprintf ("%{$this->max_t}s","_");

        //////// Calculate maximum X/Y axis label height and width:

        // TTFonts:
        if ($this->use_ttf) 
		{
            // Maximum X axis label height
            $size = $this->TTFBBoxSize($this->x_label_font['size'], $this->x_label_angle, $this->x_label_font['font'], $xlab);
            $this->x_tick_label_height = $size[1];

            // Maximum Y axis label width
            $size = $this->TTFBBoxSize($this->y_label_font['size'], $this->y_label_angle, $this->y_label_font['font'], $ylab);
            $this->y_tick_label_width = $size[0];
        }
        // Fixed fonts:
        else 
		{

            // Maximum X axis label height
            if ($this->x_label_angle == 90)
                $this->x_tick_label_height = strlen($xlab) * $this->x_label_font['width'];
            else
                $this->x_tick_label_height = $this->x_label_font['height'];

            // Maximum Y axis label width
            $this->y_tick_label_width = strlen($ylab) * $this->y_label_font['width'];
        }

        ///////// Calculate margins:

        // Upper title, ticks and tick labels, and data labels:
        $this->y_top_margin = $this->title_height + $this->safe_margin * 2;

        if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both')
            $this->y_top_margin += $this->x_title_height + $this->safe_margin;

        if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both')
            $this->y_top_margin += $this->x_tick_label_height;

        if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both')
            $this->y_top_margin += $this->x_tick_length * 2;

        if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both')
            $this->y_top_margin += $this->x_tick_label_height;

        // Lower title, ticks and tick labels, and data labels:
        $this->y_bot_margin = $this->safe_margin * 2;

        if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both')
            $this->y_bot_margin += $this->x_title_height;

        if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both')
            $this->y_bot_margin += $this->x_tick_length * 2;

        if ($this->x_tick_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0))
            $this->y_bot_margin += $this->x_tick_length * 2;

        if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both')            
            $this->y_bot_margin += $this->x_tick_label_height;

        if ($this->x_tick_label_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0))
            $this->y_bot_margin += $this->x_tick_label_height;

        if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both')
            $this->y_bot_margin += $this->x_tick_label_height;

        // Left title, ticks and tick labels:
        $this->x_left_margin = $this->safe_margin * 2;

        if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both')
            $this->x_left_margin += $this->y_title_width + $this->safe_margin;

        if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both')
            $this->x_left_margin += $this->y_tick_label_width;

        if ($this->y_tick_pos == 'plotleft' || $this->y_tick_pos == 'both')
            $this->x_left_margin += $this->y_tick_length * 2 ;

        // Right title, ticks and tick labels:
        $this->x_right_margin = $this->safe_margin * 2;

        if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both')
            $this->x_right_margin += $this->y_title_width + $this->safe_margin;

        if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both')
            $this->x_right_margin += $this->y_tick_label_width;

        if ($this->y_tick_pos == 'plotright' || $this->y_tick_pos == 'both')
            $this->x_right_margin += $this->y_tick_length * 2;

        $this->x_tot_margin = $this->x_left_margin + $this->x_right_margin;
        $this->y_tot_margin = $this->y_top_margin + $this->y_bot_margin;

        return;
    }

    /*!
     * Set the margins in pixels (left, right, top, bottom)
     */
    function SetMarginsPixels($which_lm, $which_rm, $which_tm, $which_bm)
    { 
        $this->x_left_margin = $which_lm;
        $this->x_right_margin = $which_rm;
        $this->x_tot_margin = $which_lm + $which_rm;

        $this->y_top_margin = $which_tm;
        $this->y_bot_margin = $which_bm;
        $this->y_tot_margin = $which_tm + $which_bm;

        $this->SetPlotAreaPixels();

        return;
    }

    /*!
     * Sets the limits for the plot area. If no arguments are supplied, uses
     * values calculated from CalcMargins();
     * Like in GD, (0,0) is upper left
     *
     * This resets the scale if SetPlotAreaWorld() was already called
     */
    function SetPlotAreaPixels($x1=NULL, $y1=NULL, $x2=NULL, $y2=NULL) 
    {
        if ($x2 && $y2) 
		{
            $this->plot_area = array($x1, $y1, $x2, $y2);
        } 
		else 
		{
            if (! isset($this->x_tot_margin))
                $this->CalcMargins();

            $this->plot_area = array($this->x_left_margin, $this->y_top_margin, $this->image_width - $this->x_right_margin, $this->image_height - $this->y_bot_margin);
        }
        $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
        $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];

        // Reset the scale with the new plot area.
        if (isset($this->plot_max_x))
            $this->CalcTranslation();

        return TRUE;

    }

    /*!
     * Sets minimum and maximum x and y values in the plot using FindDataLimits()
     * or from the supplied parameters, if any.
     *
     * This resets the scale if SetPlotAreaPixels() was already called
     */
    function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL) 
    {
        if (! isset($this->data_limits_done)) 
		{ // For automatic setting of data we need data limits
            $this->FindDataLimits() ;
        }
 
        if ($xmin === NULL || $xmin === '') 
		{
            if ($this->data_type == 'text-data')  // Valid for data without X values only.
                $xmin = 0;
            else
                $xmin = $this->min_x;
        }
        if ($xmax === NULL || $xmax === '') 
		{
            if ($this->data_type == 'text-data')  // Valid for data without X values only.
                $xmax = $this->max_x + 1;
            else
                $xmax = $this->max_x;
        }

        // Leave room above and below the highest and lowest data points.
        
        if ($ymin === NULL || $ymin === '') 
		{
            if ($this->min_y < 0)
                $ymin = ceil($this->min_y * 1.1);
            else
                $ymin = floor($this->min_y * 0.9);
        }    
        if ($ymax === NULL || $ymax === '') 
		{
            if ($this->max_y < 0)
                $ymax = floor($this->max_y * 0.9);
            else
                $ymax = ceil($this->max_y * 1.1);
        }
        
        // Error checking
        
        if ($ymin == $ymax)     // Minimum height
            $ymax += 1;

        if ($this->yscale_type == 'log') 
		{
            if ($ymin <= 0) 
			{ 
                $ymin = 1;
            }
            if ($ymax <= 0) {
                $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0');
                return FALSE;
            }
        }
        
        if ($ymax <= $ymin) 
		{
            $this->DrawError('SetPlotAreaWorld(): Error in data - max not greater than min');
            return FALSE;
        }
       
        // Reset (if it was already set) the scale with the new maxs and mins
      
        $this->plot_min_x = $xmin;
        $this->plot_max_x = $xmax;
        $this->plot_min_y = $ymin;
        $this->plot_max_y = $ymax;

        if (isset($this->plot_area_width)) 
		{
            $this->CalcTranslation();
        }

        return TRUE;
    } //function SetPlotAreaWorld


    /*!
     * For bar plots, which have equally spaced x variables.
     */
    function CalcBarWidths() 
    {
        $group_width = ($this->plot_area[2] - $this->plot_area[0]) / $this->num_data_rows * $this->group_frac_width;
        if ($this->plot_type == 'bars') 
		{
            $this->record_bar_width = $group_width / $this->records_per_group;
        } 
		else if ($this->plot_type == 'stackedbars') 
		{
            $this->record_bar_width = $group_width;
        }            
        $this->data_group_space = $group_width / 2;
        return TRUE;
    }

    /*!
     * Calculates scaling stuff...
     */
    function CalcTranslation()
    {
        if ($this->plot_max_x - $this->plot_min_x == 0) 
		{ // Check for div by 0
            $this->xscale = 0;
        } 
		else 
		{
            if ($this->xscale_type == 'log') 
			{
                $this->xscale = ($this->plot_area_width)/(log10($this->plot_max_x) - log10($this->plot_min_x));
            } 
			else 
			{
                $this->xscale = ($this->plot_area_width)/($this->plot_max_x - $this->plot_min_x);
            }
        }

        if ($this->plot_max_y - $this->plot_min_y == 0) 
		{ // Check for div by 0
            $this->yscale = 0;
        } 
		else 
		{
            if ($this->yscale_type == 'log') 
			{
                $this->yscale = ($this->plot_area_height)/(log10($this->plot_max_y) - log10($this->plot_min_y));
            } 
			else 
			{
                $this->yscale = ($this->plot_area_height)/($this->plot_max_y - $this->plot_min_y);
            }
        }
        // GD defines x = 0 at left and y = 0 at TOP so -/+ respectively
        if ($this->xscale_type == 'log') 
		{
            $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * log10($this->plot_min_x) );
        } 
		else 
		{
            $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * $this->plot_min_x);
        }
        if ($this->yscale_type == 'log') 
		{
            $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y));
        } 
		else 
		{ 
            $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y);
        }

        $this->scale_is_set = TRUE;

        /************** FIXME?? *************/
        // There should be a better place for this.

        // User provided y axis position?
        if ($this->y_axis_position != '') 
		{
            // Make sure we draw our axis inside the plot
            $this->y_axis_position = ($this->y_axis_position < $this->plot_min_x)
                                     ? $this->plot_min_x : $this->y_axis_position;
            $this->y_axis_position = ($this->y_axis_position > $this->plot_max_x)
                                     ? $this->plot_max_x : $this->y_axis_position;
            $this->y_axis_x_pixels = $this->xtr($this->y_axis_position);
        } 
		else 
		{
            // Default to left axis
            $this->y_axis_x_pixels = $this->xtr($this->plot_min_x);
        }
        // User provided x axis position?
        if ($this->x_axis_position != '') 
		{
            // Make sure we draw our axis inside the plot
            $this->x_axis_position = ($this->x_axis_position < $this->plot_min_y)
                                     ? $this->plot_min_y : $this->x_axis_position;
            $this->x_axis_position = ($this->x_axis_position > $this->plot_max_y)
                                     ? $this->plot_max_y : $this->x_axis_position;
            $this->x_axis_y_pixels = $this->ytr($this->x_axis_position);
        } 
		else 
		{ 
            if ($this->yscale_type == 'log')
                $this->x_axis_y_pixels = $this->ytr(1);
            else
                // Default to axis at 0 or plot_min_y (should be 0 anyway, from SetPlotAreaWorld())
                $this->x_axis_y_pixels = ($this->plot_min_y <= 0) && (0 <= $this->plot_max_y) 
                                         ? $this->ytr(0) : $this->ytr($this->plot_min_y);
        }

    } // function CalcTranslation()


    /*!
     * Translate X world coordinate into pixel coordinate
     * Needs values calculated by _CalcTranslation()
     */
    function xtr($x_world) 
    {
        //$x_pixels =  $this->x_left_margin + ($this->image_width - $this->x_tot_margin)*
        //      (($x_world - $this->plot_min_x) / ($this->plot_max_x - $this->plot_min_x)) ;
        //which with a little bit of math reduces to ...
        if ($this->xscale_type == 'log') 
		{ 
            $x_pixels = $this->plot_origin_x + log10($x_world) * $this->xscale ;
        } 
		else 
		{ 
            $x_pixels = $this->plot_origin_x + $x_world * $this->xscale ;
        }
        return ($x_pixels);
    }


    /*!
     * Translate Y world coordinate into pixel coordinate.
     * Needs values calculated by _CalcTranslation()
     */
    function ytr($y_world) 
    {
        if ($this->yscale_type == 'log') 
		{
            //minus because GD defines y = 0 at top. doh!
            $y_pixels =  $this->plot_origin_y - log10($y_world) * $this->yscale ;
        } 
		else 
		{ 
            $y_pixels =  $this->plot_origin_y - $y_world * $this->yscale ;  
        }
        return round($y_pixels);
    }

    /*!
     * Formats a tick or data label.
     *
     * \note Time formatting suggested by Marlin Viss
     */
    function FormatLabel($which_pos, $which_lab)
    {
        switch ($which_pos) 
		{
			case 'x':
			case 'plotx':
				switch ($this->x_label_type) 
				{
					case 'title':
						$lab = @ $this->data[$which_lab][0];
						break;
					case 'data':
						$lab = number_format($which_lab, $this->x_precision, '.', ',').$this->data_units_text;
						break;
					case 'time':
						$lab = strftime($this->x_time_format, $which_lab);
						break;
					default:
						// Unchanged from whatever format it is passed in
						$lab = $which_lab;
					break;
				}
	            break;
			case 'y':
			case 'ploty':
				switch ($this->y_label_type) 
				{
					case 'data':
						$lab = number_format($which_lab, $this->y_precision, '.', ',').$this->data_units_text;
						break;
					case 'time':
						$lab = strftime($this->y_time_format, $which_lab);
						break;
					default:
						// Unchanged from whatever format it is passed in
						$lab = $which_lab;
						break;
				}
				break;
			default:
				$this->PrintError("FormatLabel(): Unknown label type $which_type");
				return NULL;
        }

        return $lab;
    } //function FormatLabel



/////////////////////////////////////////////
////////////////////          GENERIC DRAWING
/////////////////////////////////////////////

    /*!
     * Fills the background.
     */
    function DrawBackground()
    {
        // Don't draw this twice if drawing two plots on one image
        if (! $this->background_done) 
		{
            if (isset($this->bgimg)) 
			{    // If bgimg is defined, use it
                $this->tile_img($this->bgimg, 0, 0, $this->image_width, $this->image_height, $this->bgmode);
            } 
			else 
			{                        // Else use solid color
                ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, $this->ndx_bg_color);
            }
            $this->background_done = TRUE;
            return TRUE;        // Done
        }
        return FALSE;           // Nothing done
    }


    /*!
     * Fills the plot area background.
     */
    function DrawPlotAreaBackground()
    {
        if (isset($this->plotbgimg)) 
		{
            $this->tile_img($this->plotbgimg, $this->plot_area[0], $this->plot_area[1], $this->plot_area_width, $this->plot_area_height, $this->plotbgmode);
        }
        else 
		{
            if ($this->draw_plot_area_background) 
			{
                ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1], $this->plot_area[2], $this->plot_area[3], $this->ndx_plot_bg_color);
            }
        }

        return TRUE;
    }


    /*!
     * Tiles an image at some given coordinates.
     *
     * \param $file   string Filename of the picture to be used as tile.
     * \param $xorig  int    X coordinate of the plot where the tile is to begin.
     * \param $yorig  int    Y coordinate of the plot where the tile is to begin.
     * \param $width  int    Width of the area to be tiled.
     * \param $height int    Height of the area to be tiled.
     * \param $mode   string One of 'centeredtile', 'tile', 'scale'.
     */
    function tile_img($file, $xorig, $yorig, $width, $height, $mode)
    {
        $size = getimagesize($file);
        $input_format = $size[2];

        switch($input_format) 
		{
			case 1:
				$im = @ imagecreatefromGIF ($file);
				if (! $im) 
				{
					$this->PrintError("tile_img:() Unable to open $file as a GIF.");
					return FALSE;
				}
				break;
			case 2:
				$im = @ imagecreatefromJPEG ($file);
				if (! $im) 
				{
					$this->PrintError("tile_img(): Unable to open $file as a JPG.");
					return FALSE;
				}
				break;
			case 3:
				$im = @ imagecreatefromPNG ($file);
				if (! $im) 
				{
					$this->PrintError("tile_img(): Unable to open $file as a PNG.");
					return FALSE;
				}
				break;
			default:
				$this->PrintError('tile_img(): Please select a gif, jpg, or png image.');
				return FALSE;
				break;
        }


        if ($mode == 'scale') 
		{
            imagecopyresized($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, $size[0],$size[1]);
            return TRUE;
        } 
		else if ($mode == 'centeredtile') 
		{
            $x0 = - floor($size[0]/2);   // Make the tile look better
            $y0 = - floor($size[1]/2);
        } 
		else if ($mode = 'tile') 
		{
            $x0 = 0;
            $y0 = 0;
        }

        // Actually draw the tile

        // But first on a temporal image.
        $tmp = ImageCreate($width, $height);
        if (! $tmp)
            $this->PrintError('tile_img(): Could not create image resource.');

        for ($x = $x0; $x < $width; $x += $size[0])
            for ($y = $y0; $y < $height; $y += $size[1])
                imagecopy($tmp, $im, $x, $y, 0, 0, $size[0], $size[1]);

        // Copy the temporal image onto the final one.
        imagecopy($this->img, $tmp, $xorig, $yorig, 0,0, $width, $height);

        // Free resources
        imagedestroy($tmp);
        imagedestroy($im);

        return TRUE;
    }  // function tile_img


    /*!
     * Draws a border around the final image.
     */
    function DrawImageBorder()
    {
        switch ($this->image_border_type) 
		{
			case 'raised':
				ImageLine($this->img, 0, 0, $this->image_width-1, 0, $this->ndx_i_border);
				ImageLine($this->img, 1, 1, $this->image_width-2, 1, $this->ndx_i_border);
				ImageLine($this->img, 0, 0, 0, $this->image_height-1, $this->ndx_i_border);
				ImageLine($this->img, 1, 1, 1, $this->image_height-2, $this->ndx_i_border);
				ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1,
						  $this->image_height-1, $this->ndx_i_border_dark);
				ImageLine($this->img, 0, $this->image_height-1, $this->image_width-1,
						  $this->image_height-1, $this->ndx_i_border_dark);
				ImageLine($this->img, $this->image_width-2, 1, $this->image_width-2,
						  $this->image_height-2, $this->ndx_i_border_dark);
				ImageLine($this->img, 1, $this->image_height-2, $this->image_width-2,
						  $this->image_height-2, $this->ndx_i_border_dark);
				break;
			case 'plain':
				ImageLine($this->img, 0, 0, $this->image_width, 0, $this->ndx_i_border_dark);
				ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1,
						  $this->image_height, $this->ndx_i_border_dark);
				ImageLine($this->img, $this->image_width-1, $this->image_height-1, 0, $this->image_height-1,
						  $this->ndx_i_border_dark);
				ImageLine($this->img, 0, 0, 0, $this->image_height, $this->ndx_i_border_dark);
				break;
			case 'none':
				break;
			default:
				$this->DrawError("DrawImageBorder(): unknown image_border_type: '$this->image_border_type'");
				return FALSE;
        }
        return TRUE;
    }


    /*!
     * Adds the title to the graph.
     */
    function DrawTitle() 
    {
        // Center of the plot area
        //$xpos = ($this->plot_area[0] + $this->plot_area_width )/ 2;

        // Center of the image:
        $xpos = $this->image_width / 2;

        // Place it at almost at the top
        $ypos = $this->safe_margin;

        $this->DrawText($this->title_font, $this->title_angle, $xpos, $ypos, $this->ndx_title_color, $this->title_txt, 'center', 'bottom'); 

        return TRUE; 

    }


    /*!
     * Draws the X-Axis Title
     */
    function DrawXTitle()
    {
        if ($this->x_title_pos == 'none')
            return;

        // Center of the plot
        $xpos = ($this->plot_area[2] + $this->plot_area[0]) / 2;

        // Upper title
        if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both') 
		{
            $ypos = $this->safe_margin + $this->title_height + $this->safe_margin;
            $this->DrawText($this->x_title_font, $this->x_title_angle, $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center');
        }
        // Lower title
        if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both') 
		{
            $ypos = $this->image_height - $this->x_title_height - $this->safe_margin;
            $this->DrawText($this->x_title_font, $this->x_title_angle, $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center');
        }
        return TRUE;
    }

    /*!
     * Draws the Y-Axis Title
     */
    function DrawYTitle()
    {
        if ($this->y_title_pos == 'none')
            return;

        // Center the title vertically to the plot
        $ypos = ($this->plot_area[3] + $this->plot_area[1]) / 2;

        if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both') 
		{
            $xpos = $this->safe_margin;
            $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color, $this->y_title_txt, 'left', 'center');
        }
        if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') 
		{
            $xpos = $this->image_width - $this->safe_margin - $this->y_title_width - $this->safe_margin;
            $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color, $this->y_title_txt, 'left', 'center');
        }

        return TRUE;
    }


    /*
     * \note Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis()
     */
    function DrawYAxis()
    {
        // Draw ticks, labels and grid, if any
        $this->DrawYTicks();

        // Draw Y axis at X = y_axis_x_pixels
        ImageLine($this->img, $this->y_axis_x_pixels, $this->plot_area[1],
                  $this->y_axis_x_pixels, $this->plot_area[3], $this->ndx_grid_color);

        return TRUE;
    }

    /*
     *
     */
    function DrawXAxis()
    {
        // Draw ticks, labels and grid
        $this->DrawXTicks();

        /* This tick and label tend to overlap with regular Y Axis labels,
         * as Mike Pullen pointed out.
         *
        //Draw Tick and Label for X axis
        if (! $this->skip_bottom_tick) {
            $ylab =$this->FormatLabel('y', $this->x_axis_position);
            $this->DrawYTick($ylab, $this->x_axis_y_pixels);
        }
        */
        //Draw X Axis at Y = x_axis_y_pixels
        ImageLine($this->img, $this->plot_area[0]+1, $this->x_axis_y_pixels, $this->plot_area[2]-1, $this->x_axis_y_pixels, $this->ndx_grid_color);

        return TRUE;
    }

    /*!
     * Draw Just one Tick, called from DrawYTicks() and DrawXAxis()
     * TODO? Move this inside DrawYTicks() and Modify DrawXAxis() ?
     */
    function DrawYTick($which_ylab, $which_ypix)
    {
        // Ticks on Y axis
        if ($this->y_tick_pos == 'yaxis') 
		{
            ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix, $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color);
        }

        // Labels on Y axis
        if ($this->y_tick_label_pos == 'yaxis') 
		{
            $this->DrawText($this->y_label_font, $this->y_label_angle,
                            $this->y_axis_x_pixels - $this->y_tick_length * 1.5, $which_ypix,
                            $this->ndx_text_color, $which_ylab, 'right', 'center');
        }

        // Ticks to the left of the Plot Area
        if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both') ) 
		{
            ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length,
                      $which_ypix, $this->plot_area[0] + $this->y_tick_cross,
                      $which_ypix, $this->ndx_tick_color);
        }

        // Ticks to the right of the Plot Area
        if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both') ) 
		{
            ImageLine($this->img, ($this->plot_area[2] + $this->y_tick_length),
                      $which_ypix, $this->plot_area[2] - $this->y_tick_cross,
                      $which_ypix, $this->ndx_tick_color);
        }

        // Labels to the left of the plot area
        if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both') 
		{
            $this->DrawText($this->y_label_font, $this->y_label_angle,
                            $this->plot_area[0] - $this->y_tick_length * 1.5, $which_ypix,
                            $this->ndx_text_color, $which_ylab, 'right', 'center');
        }
        // Labels to the right of the plot area
        if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both') 
		{
            $this->DrawText($this->y_label_font, $this->y_label_angle,
                            $this->plot_area[2] + $this->y_tick_length * 1.5, $which_ypix,
                            $this->ndx_text_color, $which_ylab, 'left', 'center');
        }
   } // Function DrawYTick()


    /*!
     * Draws Grid, Ticks and Tick Labels along Y-Axis
     * Ticks and ticklabels can be left of plot only, right of plot only,
     * both on the left and right of plot, or crossing a user defined Y-axis
     * TODO: marks at whole numbers (-10, 10, 20, 30 ...) no matter where the plot begins (-3, 4.7, etc.)
     */
    function DrawYTicks()
    {
        // Sets the line style for IMG_COLOR_STYLED lines (grid)
        if ($this->dashed_grid) 
		{
            $this->SetDashedStyle($this->ndx_light_grid_color);
            $style = IMG_COLOR_STYLED;
        } 
		else 
		{
            $style = $this->ndx_light_grid_color;
        }

        // maxy is always > miny so delta_y is always positive
        if ($this->y_tick_inc) 
		{
            $delta_y = $this->y_tick_inc;
        } 
		elseif ($this->num_y_ticks) 
		{
            $delta_y = ($this->plot_max_y - $this->plot_min_y) / $this->num_y_ticks;
        } 
		else 
		{
            $delta_y = ($this->plot_max_y - $this->plot_min_y) / 10 ;
        }

        // NOTE: When working with floats, because of approximations when adding $delta_y,
        // $y_tmp never equals $y_end  at the for loop, so one spurious line would  get drawn where
        // not for the substraction to $y_end here.
        $y_tmp = (double)$this->plot_min_y;
        $y_end = (double)$this->plot_max_y - ($delta_y/2);

        if ($this->skip_bottom_tick)
            $y_tmp += $delta_y;

        if ($this->skip
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.