Login   Register  
PHP Classes
elePHPant
Icontem

File: class.diagram.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Diogo Resende  >  Diagram 2  >  class.diagram.php  >  Download  
File: class.diagram.php
Role: Class source
Content type: text/plain
Description: Main class
Class: Diagram 2
Draw an hierarchic diagram from a XML definition
Author: By
Last change: Bug fixes
Date: 9 years ago
Size: 35,728 bytes
 

Contents

Class file image Download
<?php
    /**
     * Diagram
     * Class to draw diagrams from a xml file/data
     *
     * @author Diogo Resende <me@diogoresende.net>
     * @version 2.0
     * @
     *
     **/
    class Diagram {
        var $prepared = false;
        
        var $padding = array(
            'top'    => 2,
            'left'   => 2,
            'right'  => 2,
            'bottom' => 2
        );
        var $margin = array(
            'top'    => 10,
            'left'   => 10,
            'right'  => 10,
            'bottom' => 10
        );
        var $border = array(
            'top'        => 1,
            'left'       => 1,
            'right'      => 1,
            'bottom'     => 1,
            'middle'     => 1,
            'connection' => 1
        );
        var $color = array(
            'background' => '#f',
            'border'     => '#0',
            'connection' => '#0',
            'name'       => array(
                'background' => '#f4f4f4',
                'text'       => '#0',
                'connection' => '#0'
            ),
            'data'       => array(
                'background' => '#f0f0f0',
                'text'       => '#0'
            )
        );
        var $font = array(
            'name'       => 3,
            'data'       => 2,
            'connection' => 2
        );
        var $align = array(
            'name' => 'left',
            'data' => 'left'
        );
        var $width = 100;
        
        /**
         * If this is true, the node will have the width necessary
         * to show the name in only one line. It will still have the
         * normal width if the name is small
         **/
        var $fit_name = true;

        /**
         * If zero, no arrow is drawed, if greather than zero, an
         * arrow will be drawed with that size
         **/
        var $arrow_thickness = 3;
        
        /**
         * Diagram::Diagram()
         *
         * Class constructor
         **/
        function Diagram($xml_file = '') {
            if (strlen($xml_file) > 0) {
                $this->loadXmlFile($xml_file);
            }
        }

        /**
         * Diagram::loadXmlFile()
         *
         * Load data from XML file
         **/
        function loadXmlFile($file) {
            if (($fp = fopen($file, 'r')) !== false) {
                $data = '';
                while (!feof($fp)) {
                    $data .= fread($fp, 4096);
                }
                fclose($fp);

                $this->loadXmlData($data);
            }
        }

        /**
         * Diagram::loadXmlData()
         *
         * Load data from a XML string
         **/
        function loadXmlData($data) {
            if (!class_exists('XmlParser')) {
                /*...............**/
                require dirname(__FILE__) . '/class.xmlparser.php';
            }
            $xml = new XmlParser($data);

            $this->struct = $xml->struct;

            $xml = null;
            
            $this->prepared = false;
        }
        
        /**
         * Diagram::loadFromArray()
         *
         * Load data from an associative array
         **/
        function loadFromArray($array) {
            $this->struct = array(array('childs' => array()));
            $this->__addArrayToNode($this->struct[0]['childs'], $array);
        }

        /**
         * Diagram::setDefaultBorder()
         *
         * Set border sizes. You can omit borders. The ones available are:
         * - left,top,right,bottom,middle,connection
         *
         * Example: setDefaultBorder(array('left' => 2, 'right' => 2));
         **/
        function setDefaultBorder($borders) {
            foreach ($borders as $k => $v) {
                if (isset($this->border[$k])) {
                    $this->border[$k] = (int) $v;
                }
            }
        }
        
        /**
         * Diagram::setDefaultFont()
         *
         * Set fonts. You can omit fonts. The ones available are:
         * - name,data,connection
         *
         * Example: setDefaultFont(array('connection' => 1, 'name' => 2, 'data' => 3));
         **/
        function setDefaultFont($fonts) {
            foreach ($fonts as $k => $v) {
                if (isset($this->font[$k])) {
                    $this->font[$k] = $v;
                }
            }
        }
        
        /**
         * Diagram::setDefaultAlign()
         *
         * Set alignments. You can omit alignments. The ones available are:
         * - name,data
         * The possible alignments are:
         * - left,center,right
         *
         * Example: setDefaultAlign(array('name' => 'center'));
         **/
        function setDefaultAlign($aligns) {
            foreach ($aligns as $k => $v) {
                if (isset($this->align[$k])) {
                    $this->align[$k] = $v;
                }
            }
        }

        /**
         * Diagram::setDefaultColor()
         *
         * Set colors. You can omit colors. The ones available are:
         * - background,border,connection
         *
         * Example: setDefaultColor(array('background' => '#ffffff', 'border' => '#000'));
         **/
        function setDefaultColor($colors) {
            foreach ($colors as $k => $v) {
                if (isset($this->color[$k])) {
                    $this->color[$k] = $v;
                }
            }
        }
        
        /**
         * Diagram::setDefaultDataColor()
         *
         * Set data colors. You can omit colors. The ones available are:
         * - background,text
         *
         * Example: setDefaultDataColor(array('text' => '#000'));
         **/
        function setDefaultDataColor($colors) {
            foreach ($colors as $k => $v) {
                if (isset($this->color['data'][$k])) {
                    $this->color['data'][$k] = $v;
                }
            }
        }
        
        /**
         * Diagram::setDefaultNameColor()
         *
         * Set name colors. You can omit colors. The ones available are:
         * - background,text,connection
         *
         * Example: setDefaultNameColor(array('text' => '#000'));
         **/
        function setDefaultNameColor($colors) {
            foreach ($colors as $k => $v) {
                if (isset($this->color['name'][$k])) {
                    $this->color['name'][$k] = $v;
                }
            }
        }

        /**
         * Diagram::Draw()
         *
         * Draw all the diagram and output it to stdout
         * or a file
         **/
        function Draw($output_file = '') {
            if (!$this->prepared) {
                $this->__prepare($this->struct[0], true);
                $this->prepared = true;
            }

            $w = $this->struct[0]['attrib']['TOTALWIDTH'];
            $h = $this->struct[0]['attrib']['CHILDHEIGHT'];
            
            $im = imagecreatetruecolor($w, $h);

            $this->__drawBackground($im, $this->struct[0]['attrib'], $w, $h);
            $this->__drawNode($im, $this->struct[0]['childs'][0], 0, 0, $this->struct[0]['childs'][0]['attrib']['NODEHEIGHT']);
            
            /**
             * Output
             **/
            if (strlen($output_file) > 0 && is_dir(dirname($output_file))) {
                imagepng($im, $output_file);
            } else {
                header('Content-Type: image/png');
                imagepng($im);
            }
        }

        /**
         * Diagram::__addArrayToNode()
         *
         * Load an array to a node with default values
         **/
        function __addArrayToNode(&$node_childs, &$array) {
            foreach ($array as $k => $v) {
                if (is_array($v)) {
                    $node_childs[] = array(
                        'tag'    => 'NODE',
                        'attrib' => array(),
                        'data'   => $k,
                        'childs' => array()
                    );
                    $this->__addArrayToNode($node_childs[count($node_childs) - 1]['childs'], $v);
                } else {
                    $node_childs[] = array(
                        'tag'    => 'NODE',
                        'attrib' => array(),
                        'data'   => $v,
                        'childs' => array()
                    );
                }
            }
        }

        /**
         * Diagram::__drawBackground()
         *
         * Draw diagram background
         **/
        function __drawBackground(&$im, &$diagram_atr, $w, $h) {
            if (isset($diagram_atr['BGCOLOR2'])) {
                $this->__drawDegrade($im, 0, 0, $w, $h, $diagram_atr['BGCOLOR'], $diagram_atr['BGCOLOR2']);
            } else {
                $c = $this->__allocateColor($im, $diagram_atr['BGCOLOR']);
                
                imagefilledrectangle($im, 0, 0, $w, $h, $c);
            }
        }

        /**
         * Diagram::__drawNode()
         *
         * Draw a node
         **/
        function __drawNode(&$im, &$node, $left = 0, $top = 0, $total_height = 0, $parent_tail_left = null, $parent_tail_top = null) {
            $x = round(($node['attrib']['TOTALWIDTH'] - $node['attrib']['NODEWIDTH']) / 2);
            
            $x += $left + $node['attrib']['MARGINLEFT'];
            $y = $top + $node['attrib']['MARGINTOP'];
            
            $this->__drawNodeBackground($im, $node['attrib'], $x, $y, (strlen($node['data']) > 0));
            $this->__drawNodeBorder($im, $node['attrib'], $x, $y, (strlen($node['data']) > 0));
            
            if ($parent_tail_left != null) {
                $this->__drawNodeParentConnection($im,
                                                  $node,
                                                  $x + round($node['attrib']['WIDTH'] / 2),
                                                  $y,
                                                  $parent_tail_left,
                                                  $parent_tail_top,
                                                  $node['attrib']['CONNECTIONCOLOR'],
                                                  $node['attrib']['BORDERCONNECTION'],
                                                  $node['attrib']['ARROW']);
            }
            $this->__drawNodeData($im, $node, $x, $y);

            $node_tail_left = ($left + round($node['attrib']['TOTALWIDTH'] / 2));
            $node_tail_top = ($top + $node['attrib']['NODEHEIGHT'] - $node['attrib']['MARGINBOTTOM']);

            $left_offset = $left;
            $top_offset = $top + $total_height;
            for ($i = 0; $i < count($node['childs']); $i++) {
                $this->__drawNode($im, $node['childs'][$i], $left_offset, $top_offset, $node['attrib']['TOTALHEIGHT'], $node_tail_left, $node_tail_top);
                
                $left_offset += $node['childs'][$i]['attrib']['TOTALWIDTH'];
            }
        }

        function __drawNodeParentConnection(&$im, &$node, $node_tail_left, $node_tail_top, $parent_tail_left, $parent_tail_top, $color, $thickness, $arrow_thickness) {
            $offset = round($thickness / 2);
            $middle = round(($node_tail_top - $parent_tail_top) / 2);
            
            $c = $this->__allocateColor($im, $color);
            
            for ($i = 0; $i < $thickness; $i++) {
                imageline($im, $parent_tail_left - $offset + $i, $parent_tail_top, $parent_tail_left - $offset + $i, $parent_tail_top + $middle, $c);
            }
            
            for ($i = 0; $i < $thickness; $i++) {
                imageline($im, $parent_tail_left - $offset, $parent_tail_top + $middle - $i, $node_tail_left - $offset, $parent_tail_top + $middle - $i, $c);
            }
            $j = $i;

            for ($i = 0; $i < $thickness; $i++) {
                imageline($im, $node_tail_left - $offset + $i, $parent_tail_top + $middle - $j + 1, $node_tail_left - $offset + $i, $node_tail_top - $arrow_thickness, $c);
            }
            
            /**
             * Arrow
             **/
            imagesetpixel($im, $node_tail_left - 1, $node_tail_top - 1, $c);
            $offset += $arrow_thickness - 1;
            for ($i = 0; $i < $offset; $i++) {
                imageline($im, $node_tail_left - 2 - $i, $node_tail_top - 2 - $i, $node_tail_left + $i, $node_tail_top - 2 - $i, $c);
            }

            /**
             * Connection text
             **/
            if (isset($node['attrib']['CONNECTIONNAME']) && strlen($node['attrib']['CONNECTIONNAME']) > 0) {
                if ($parent_tail_left > $node_tail_left) {
                    $tmp = $parent_tail_left;
                    $parent_tail_left = $node_tail_left;
                    $node_tail_left = $tmp;
                }
                $this->__drawText($im, $parent_tail_left, $parent_tail_top + $middle - $thickness - imagefontheight($node['attrib']['CONNECTIONFONT']), ($node_tail_left - $parent_tail_left), $node['attrib']['CONNECTIONFONT'], $node['attrib']['CONNECTIONNAMECOLOR'], 'center', $node['attrib']['CONNECTIONNAME']);
            }
        }

        /**
         * Diagram::__drawNodeData()
         *
         * Draw the text on the node
         **/
        function __drawNodeData(&$im, &$node, $left, $top) {
            $left += $node['attrib']['BORDERLEFT'] + $node['attrib']['PADDINGLEFT'];
            $top += $node['attrib']['BORDERTOP'] + $node['attrib']['PADDINGTOP'];
            if (isset($node['attrib']['NAME'])) {
                $top = $this->__drawText($im, $left, $top, $node['attrib']['WIDTH'], $node['attrib']['NAMEFONT'], $node['attrib']['NAMECOLOR'], $node['attrib']['NAMEALIGN'], $node['attrib']['NAME']);
                
                $top += $node['attrib']['PADDINGBOTTOM'] + $node['attrib']['BORDERMIDDLE'] + $node['attrib']['PADDINGTOP'];
            }
            if (strlen($node['data']) > 0) {
                $this->__drawText($im, $left, $top, $node['attrib']['WIDTH'], $node['attrib']['FONT'], $node['attrib']['COLOR'], $node['attrib']['ALIGN'], $node['data']);
            }
        }
        
        /**
         * Diagram::__drawText()
         *
         * Draw text on a certain position with a maximum with, font
         * and color
         * Returns the new vertical position
         **/
        function __drawText(&$im, $x, $y, $max_width, $font, $color, $align, $text) {
            $text = explode("\n", wordwrap($text, floor($max_width / imagefontwidth($font)), "\n"));
            $c = $this->__allocateColor($im, $color);
            for ($i = 0; $i < count($text); $i++) {
                switch ($align) {
                    case 'center':
                        imagestring($im, $font, $x + round(($max_width - (imagefontwidth($font) * strlen($text[$i]))) / 2), $y, $text[$i], $c);
                        break;
                    case 'right':
                        imagestring($im, $font, $x + $max_width - (imagefontwidth($font) * strlen($text[$i])), $y, $text[$i], $c);
                        break;
                    default:
                        imagestring($im, $font, $x, $y, $text[$i], $c);
                }
                $y += imagefontheight($font);
            }
            return $y;
        }
        
        /**
         * Diagram::__drawNodeBorder()
         *
         * Draw the node border
         **/
        function __drawNodeBorder(&$im, &$node_atr, $left, $top, $has_data) {
            $width = $node_atr['BORDERLEFT'] + $node_atr['PADDINGLEFT'] + $node_atr['WIDTH'] + $node_atr['PADDINGRIGHT'];
            $height = $node_atr['BORDERTOP'] + $node_atr['PADDINGTOP'] + $node_atr['HEIGHT'] + $node_atr['PADDINGBOTTOM'];
            
            $c = $this->__allocateColor($im, $node_atr['BORDERCOLOR']);

            for ($i = 0; $i < $node_atr['BORDERLEFT']; $i++) {
                imageline($im, $left + $i, $top, $left + $i, $top + $height - 1, $c);
            }
            
            for ($i = 0; $i < $node_atr['BORDERTOP']; $i++) {
                imageline($im, $left, $top + $i, $left + $width - 1, $top + $i, $c);
            }

            for ($i = 0; $i < $node_atr['BORDERRIGHT']; $i++) {
                imageline($im, $left + $width - $i, $top, $left + $width - $i, $top + $height, $c);
            }
            
            for ($i = 0; $i < $node_atr['BORDERBOTTOM']; $i++) {
                imageline($im, $left, $top + $height + $i, $left + $width, $top + $height + $i, $c);
            }
            
            if ($has_data && isset($node_atr['NAMEHEIGHT'])) {
                $height = $node_atr['BORDERTOP'] + $node_atr['PADDINGTOP'] + $node_atr['NAMEHEIGHT'] + $node_atr['PADDINGBOTTOM'];
                for ($i = 0; $i < $node_atr['BORDERMIDDLE']; $i++) {
                    imageline($im, $left, $top + $height + $i, $left + $width, $top + $height + $i, $c);
                }
            }
        }

        /**
         * Diagram::__drawNodeBackground()
         *
         * Draw the node background(s)
         **/
        function __drawNodeBackground(&$im, &$node_atr, $left, $top, $has_data) {
            $width = $node_atr['BORDERLEFT'] + $node_atr['PADDINGLEFT'] + $node_atr['WIDTH'] + $node_atr['PADDINGRIGHT'];
            $height = $node_atr['BORDERTOP'] + $node_atr['PADDINGTOP'] + $node_atr['HEIGHT'] + $node_atr['PADDINGBOTTOM'];

            if ($has_data) {
                if (isset($node_atr['BGCOLOR2'])) {
                    $this->__drawDegrade($im, $left, $top, $left + $width, $top + $height, $node_atr['BGCOLOR'], $node_atr['BGCOLOR2']);
                } else {
                    $c = $this->__allocateColor($im, $node_atr['BGCOLOR']);
                    imagefilledrectangle($im, $left, $top, $left + $width, $top + $height, $c);
                }
            }
            if (isset($node_atr['NAMEHEIGHT'])) {
                $height = $node_atr['BORDERTOP'] + $node_atr['PADDINGTOP'] + $node_atr['NAMEHEIGHT'] + $node_atr['PADDINGBOTTOM'];

                if (isset($node_atr['NAMEBGCOLOR2'])) {
                    $this->__drawDegrade($im, $left, $top, $left + $width, $top + $height, $node_atr['NAMEBGCOLOR'], $node_atr['NAMEBGCOLOR2']);
                } else {
                    $c = $this->__allocateColor($im, $node_atr['NAMEBGCOLOR']);
                    imagefilledrectangle($im, $left, $top, $left + $width, $top + $height, $c);
                }
            }
        }

        /**
         * Diagram::__prepare()
         *
         * Prepare the diagram structure and add the individual
         * settings to every node
         **/
        function __prepare(&$node, $is_diagram = false) {
            /**
             * Padding
             **/
            if ($this->__checkParam($node['attrib'], 'PADDING')) {
                /**
                 * this ensures that if there is a global atribute for
                 * padding, it is spreaded thru all paddings
                 **/
                $this->__setParam($node['attrib'],
                                  array(
                                    'PADDINGLEFT',
                                    'PADDINGTOP',
                                    'PADDINGRIGHT',
                                    'PADDINGBOTTOM'
                                  ),
                                  $node['attrib']['PADDING']);
            }
            $this->__checkParam($node['attrib'], 'PADDINGLEFT', $this->padding['left']);
            $this->__checkParam($node['attrib'], 'PADDINGTOP', $this->padding['top']);
            $this->__checkParam($node['attrib'], 'PADDINGRIGHT', $this->padding['right']);
            $this->__checkParam($node['attrib'], 'PADDINGBOTTOM', $this->padding['bottom']);

            /**
             * Borders
             **/
            if ($this->__checkParam($node['attrib'], 'BORDER')) {
                /**
                 * this ensures that if there is a global atribute for
                 * border, it is spreaded thru all borders
                 **/
                $this->__setParam($node['attrib'],
                                  array(
                                    'BORDERLEFT',
                                    'BORDERTOP',
                                    'BORDERRIGHT',
                                    'BORDERBOTTOM',
                                    'BORDERMIDDLE',
                                    'BORDERCONNECTION'
                                  ),
                                  $node['attrib']['BORDER']);
            }
            $this->__checkParam($node['attrib'], 'BORDERLEFT', $this->border['left']);
            $this->__checkParam($node['attrib'], 'BORDERTOP', $this->border['top']);
            $this->__checkParam($node['attrib'], 'BORDERRIGHT', $this->border['right']);
            $this->__checkParam($node['attrib'], 'BORDERBOTTOM', $this->border['bottom']);
            $this->__checkParam($node['attrib'], 'BORDERMIDDLE', $this->border['middle']);
            $this->__checkParam($node['attrib'], 'BORDERCONNECTION', $this->border['connection']);

            /**
             * Margins
             **/
            if ($this->__checkParam($node['attrib'], 'MARGIN')) {
                /**
                 * this ensures that if there is a global atribute for
                 * margin, it is spreaded thru all margins
                 **/
                $this->__setParam($node['attrib'],
                                  array(
                                    'MARGINLEFT',
                                    'MARGINTOP',
                                    'MARGINRIGHT',
                                    'MARGINBOTTOM'
                                  ),
                                  $node['attrib']['MARGIN']);
            }
            $this->__checkParam($node['attrib'], 'MARGINLEFT', $this->margin['left']);
            $this->__checkParam($node['attrib'], 'MARGINTOP', $this->margin['top']);
            $this->__checkParam($node['attrib'], 'MARGINRIGHT', $this->margin['right']);
            $this->__checkParam($node['attrib'], 'MARGINBOTTOM', $this->margin['bottom']);
            if (!$is_diagram) {
                /**
                 * Alignments
                 **/
                $this->__checkParam($node['attrib'], 'NAMEALIGN', $this->align['name']);
                $this->__checkParam($node['attrib'], 'ALIGN', $this->align['data']);
            
                /**
                 * Colors
                 **/
                $this->__checkColorParam($node['attrib'], 'NAMECOLOR', $this->color['name']['text']);
                $this->__checkColorParam($node['attrib'], 'NAMEBGCOLOR', $this->color['name']['background']);
                $this->__checkColorParam($node['attrib'], 'COLOR', $this->color['data']['text']);
                $this->__checkColorParam($node['attrib'], 'BGCOLOR', $this->color['data']['background']);
                $this->__checkColorParam($node['attrib'], 'BORDERCOLOR', $this->color['border']);
                $this->__checkColorParam($node['attrib'], 'CONNECTIONCOLOR', $this->color['connection']);
                $this->__checkColorParam($node['attrib'], 'CONNECTIONNAMECOLOR', $this->color['name']['connection']);

                /**
                 * Colors (Degrade support)
                 **/
                if (isset($node['attrib']['BGCOLOR2'])) {
                    $this->__checkColorParam($node['attrib'], 'BGCOLOR2', '');
                }
                if (isset($node['attrib']['NAMEBGCOLOR2'])) {
                    $this->__checkColorParam($node['attrib'], 'NAMEBGCOLOR2', '');
                }

                /**
                 * Fonts
                 **/
                $this->__checkParam($node['attrib'], 'NAMEFONT', $this->font['name']);
                $this->__checkParam($node['attrib'], 'CONNECTIONFONT', $this->font['connection']);
                $this->__checkParam($node['attrib'], 'FONT', $this->font['data']);

                /**
                 * Misc
                 **/
                $this->__checkParam($node['attrib'], 'ARROW', $this->arrow_thickness);
                $this->__checkBooleanParam($node['attrib'], 'FITNAME', $this->fit_name);
                $this->__checkParam($node['attrib'], 'CONNECTIONNAME', '');
            } else {
                /**
                 * Colors
                 **/
                $this->__checkColorParam($node['attrib'], 'BGCOLOR', $this->color['background']);

                /**
                 * Colors (Degrade support)
                 **/
                if (isset($node['attrib']['BGCOLOR2'])) {
                    $this->__checkColorParam($node['attrib'], 'BGCOLOR2', '');
                }

                /**
                 * Global node dimensions
                 **/
                if (isset($node['attrib']['WIDTH']) && (int) $node['attrib']['WIDTH'] > 0) {
                    $this->width = (int) $node['attrib']['WIDTH'];
                }
                
                /**
                 * Global alignments
                 **/
                if (isset($node['attrib']['ALIGN'])) {
                    $this->align['data'] = $node['attrib']['ALIGN'];
                }
                if (isset($node['attrib']['NAMEALIGN'])) {
                    $this->align['name'] = $node['attrib']['NAMEALIGN'];
                }
            }
            /**
             * Node dimensions
             **/
            $this->__checkParam($node['attrib'], 'WIDTH', $this->width);
            $this->__calculateNodeWidth($node['attrib']);
            $this->__calculateNodeHeight($node['attrib'], $node['data']);

            $node_width = 0;
            $node_height = 0;
            $child_height = 0;

            for ($i = 0; $i < count($node['childs']); $i++) {
                $this->__prepare($node['childs'][$i]);

                $node_width += $node['childs'][$i]['attrib']['TOTALWIDTH'];
                if ($node['childs'][$i]['attrib']['CHILDHEIGHT'] > $child_height) {
                    $child_height = $node['childs'][$i]['attrib']['CHILDHEIGHT'];
                }
                if ($node['childs'][$i]['attrib']['NODEHEIGHT'] > $node_height) {
                    $node_height = $node['childs'][$i]['attrib']['NODEHEIGHT'];
                }
            }

            $node['attrib']['TOTALHEIGHT'] = $node_height;
            $node['attrib']['CHILDHEIGHT'] = $node['attrib']['TOTALHEIGHT'] + $child_height;

            if ($node_width > $node['attrib']['NODEWIDTH']) {
                $node['attrib']['TOTALWIDTH'] = $node_width;
            } else {
                $node['attrib']['TOTALWIDTH'] = $node['attrib']['NODEWIDTH'];
            }
        }

        /**
         * Diagram::__drawDegrade()
         *
         * Draw a degrade from a color to another inside a rectangle
         **/
        function __drawDegrade(&$im, $x1, $y1, $x2, $y2, $color_start, $color_end) {
            $steps = ($y2 - $y1);
            $inc_r = ($color_end[0] - $color_start[0]) / $steps;
            $inc_g = ($color_end[1] - $color_start[1]) / $steps;
            $inc_b = ($color_end[2] - $color_start[2]) / $steps;
            
            $c = $color_start;
            while ($y2 > $y1) {
                $c[0] += $inc_r;
                $c[1] += $inc_g;
                $c[2] += $inc_b;
                //print_r($c);
                $cl = $this->__allocateColor($im, $c);
                imageline($im, $x1, $y1, $x2, $y1++, $cl);
            }
        }
        
        /**
         * Diagram::__totalLines()
         *
         * Returns the number of lines that a certain amount of
         * text needs with a certain font and a certain maximum
         * width
         **/
        function __totalLines($text, $font, $max_width) {
            $text = wordwrap($text, floor($max_width / imagefontwidth($font)), "\n");

            return substr_count($text, "\n") + 1;
        }
        
        /**
         * Diagram::__calculateNodeWidth()
         *
         * Based on Diagram::fit_title and node atributes, this
         * function returns the width of the node (not including
         * paddings, margins or borders)
         **/
        function __calculateNodeWidth(&$node_atr) {
            if (isset($node_atr['NAME']) && strlen($node_atr['NAME']) > 0 && $node_atr['FITNAME']) {
                /**
                 * Node wants it's title to fit in one line
                 **/
                $w = strlen($node_atr['NAME']) * imagefontwidth($node_atr['NAMEFONT']);
                if ($node_atr['WIDTH'] < $w) {
                    $node_atr['WIDTH'] = $w;
                }
            } else {
                $this->__checkParam($node_atr, 'WIDTH', $this->width);
            }
            $node_atr['NODEWIDTH'] = $node_atr['MARGINLEFT'] + $node_atr['BORDERLEFT'] + $node_atr['PADDINGLEFT'] +
                                     $node_atr['WIDTH'] +
                                     $node_atr['PADDINGRIGHT'] + $node_atr['BORDERRIGHT'] + $node_atr['MARGINRIGHT'];
        }

        /**
         * Diagram::__calculateNodeHeight()
         *
         * Based on node width, it returns the height (including
         * middle border and 1 padding top and 1 padding bottom
         *   ____|____
         *  |_________| <--- from here (top of title)
         *  |         |
         *  |         |
         *  |_________| <--- to here (bottom of data)
         **/
        function __calculateNodeHeight(&$node_atr, &$node_data) {
            $height = 0;
            /**
             * Name
             **/
            if (isset($node_atr['NAME'])) {
                /**
                 * At least it will fill one line
                 **/
                $height = imagefontheight($node_atr['NAMEFONT']);
                if (!$node_atr['FITNAME']) {
                    /**
                     * Multiply the size of one line by the number of lines
                     **/
                    $height *= $this->__totalLines($node_atr['NAME'], $node_atr['NAMEFONT'], $node_atr['WIDTH']);
                }
                $this->__setParam($node_atr, 'NAMEHEIGHT', $height);
            }
            
            /**
             * Data
             **/
            if (strlen($node_data) > 0) {
                if (isset($node_atr['NAME'])) {
                    $height += $node_atr['PADDINGBOTTOM'] + $node_atr['BORDERMIDDLE'] + $node_atr['PADDINGTOP'];
                }
                $height += (imagefontheight($node_atr['FONT']) * $this->__totalLines($node_data, $node_atr['FONT'], $node_atr['WIDTH']));
            }
            
            $node_atr['HEIGHT'] = $height;
            $node_atr['NODEHEIGHT'] = $node_atr['MARGINTOP'] + $node_atr['BORDERTOP'] + $node_atr['PADDINGTOP'] +
                                      $node_atr['HEIGHT'] +
                                      $node_atr['PADDINGBOTTOM'] + $node_atr['BORDERBOTTOM'] + $node_atr['MARGINBOTTOM'];
        }
        
        /**
         * Diagram::__checkParam()
         *
         * Check for existance of a parameter in a node and atribute
         * the default one if not found.
         * If default_value is null it returns only the existance of
         * the parameter
         **/
        function __checkParam(&$atributes, $param, $default_value = null) {
            if ($default_value == null) {
                return isset($atributes[$param]);
            } elseif (!isset($atributes[$param])) {
                $atributes[$param] = $default_value;
                return false;
            }
            return true;
        }
        
        /**
         * Diagram::__checkColorParam()
         *
         * Checks the parameter with Diagram::__checkParam() and
         * then converts it to a color array
         **/
        function __checkColorParam(&$atributes, $param, $default_value) {
            if (!isset($atributes[$param])) {
                $atributes[$param] = $default_value;
            }
            $atributes[$param] = $this->__decodeRgbColor($atributes[$param]);
        }

        /**
         * Diagram::__checkBooleanParam()
         *
         * Checks the parameter with Diagram::__checkParam() and
         * then converts it to a boolean
         **/
        function __checkBooleanParam(&$atributes, $param, $default_value) {
            if (!isset($atributes[$param])) {
                $atributes[$param] = (bool) $default_value;
            } else {
                $atributes[$param] = (bool) $atributes[$param];
            }
        }

        /**
         * Diagram::__setParam()
         *
         * Set a value to a parameter or an array of parameters
         **/
        function __setParam(&$node_attributes, $atribute_name, $value) {
            if (is_array($atribute_name)) {
                for ($i = 0; $i < count($atribute_name); $i++) {
                    $node_attributes[$atribute_name[$i]] = $value;
                }
            } else {
                $node_attributes[$atribute_name] = $value;
            }
        }
        
        /**
         * Diagram::__decodeRgbColor()
         *
         * Given an RGB string (#abcdef, #abc, #a) returns an array
         * with the Red, Green and Blue componements
         **/
        function __decodeRgbColor($color) {
            if (preg_match('/^\#[0-9a-f]+$/', $color)) {
                $color = substr($color, 1);
                switch (strlen($color)) {
                    case 6:
                        return array(hexdec(substr($color, 0, 2)),
                                     hexdec(substr($color, 2, 2)),
                                     hexdec(substr($color, 4)));
                    case 3:
                        return array(hexdec($color[0] . $color[0]),
                                     hexdec($color[1] . $color[1]),
                                     hexdec($color[2] . $color[2]));
                    case 1:
                        return array(hexdec($color[0] . $color[0]),
                                     hexdec($color[0] . $color[0]),
                                     hexdec($color[0] . $color[0]));
                    default:
                        return array(0, 0, 0); // black
                }
            } else {
                return array(0, 0, 0); // black
            }
        }

        /**
         * Diagram::__allocateColor()
         *
         * Allocate a color on the diagram image or return a pointer
         * if the color is already allocated
         **/
        function __allocateColor(&$im, $color) {
            $c = imagecolorexact($im, $color[0], $color[1], $color[2]);
            if ($c >= 0) {
                return $c;
            }
            return imagecolorallocate($im, $color[0], $color[1], $color[2]);
        }
    }
?>