Alpha Framework alpha--util--graphs
[ class tree: alpha--util--graphs ] [ index: alpha--util--graphs ] [ all elements ]

Source for file AlphaTreeGraph.inc

Documentation is available at AlphaTreeGraph.inc

  1. <?php
  2.  
  3. require_once $config->get('sysRoot').'alpha/util/graphs/AlphaGraphNode.inc';
  4.  
  5. /**
  6.  *
  7.  * Maintains the geometry for a tree graph
  8.  * 
  9.  * @package alpha::util::graphs
  10.  * @since 1.0
  11.  * @author John Collins <dev@alphaframework.org>
  12.  * @version $Id: AlphaTreeGraph.inc 1341 2011-03-17 15:02:02Z johnc $
  13.  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
  14.  * @copyright Copyright (c) 2011, John Collins (founder of Alpha Framework).
  15.  *  All rights reserved.
  16.  * 
  17.  *  <pre>
  18.  *  Redistribution and use in source and binary forms, with or
  19.  *  without modification, are permitted provided that the
  20.  *  following conditions are met:
  21.  * 
  22.  *  * Redistributions of source code must retain the above
  23.  *    copyright notice, this list of conditions and the
  24.  *    following disclaimer.
  25.  *  * Redistributions in binary form must reproduce the above
  26.  *    copyright notice, this list of conditions and the
  27.  *    following disclaimer in the documentation and/or other
  28.  *    materials provided with the distribution.
  29.  *  * Neither the name of the Alpha Framework nor the names
  30.  *    of its contributors may be used to endorse or promote
  31.  *    products derived from this software without specific
  32.  *    prior written permission.
  33.  *   
  34.  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  35.  *  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  36.  *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  37.  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38.  *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  39.  *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  44.  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  45.  *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  46.  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  *  </pre>
  48.  *  
  49.  */
  50. class AlphaTreeGraph {
  51.     /**
  52.      * An array of nodes on the previous level
  53.      * 
  54.      * @var array 
  55.      * @since 1.0
  56.      */
  57.     private $previousLevelNodes array();
  58.     
  59.     /**
  60.      * An array of nodes in this graph
  61.      * 
  62.      * @var array 
  63.      * @since 1.0
  64.      */
  65.     private $nodes array();
  66.     
  67.     /**
  68.      * The root node of the graph
  69.      * 
  70.      * @var AlphaGraphNode 
  71.      * @since 1.0
  72.      */
  73.     private $root;
  74.     
  75.     /**
  76.      * The amount of space between graph rows
  77.      * 
  78.      * @var integer 
  79.      * @since 1.0
  80.      */
  81.     private $rowSpace;
  82.     
  83.     /**
  84.      * The amount of space between graph columns
  85.      * 
  86.      * @var integer 
  87.      * @since 1.0
  88.      */
  89.     private $colSpace;
  90.     
  91.     /**
  92.      * The amount of space between graph branches
  93.      * 
  94.      * @var integer 
  95.      * @since 1.0
  96.      */
  97.     private $branchSpace;
  98.     
  99.     /**
  100.      * Flag to track whether the chart is rendered or not
  101.      * 
  102.      * @var boolean 
  103.      * @since 1.0
  104.      */
  105.     private $isRendered false;
  106.     
  107.     /**
  108.      * The index of the current node in the graph we are inspecting
  109.      * 
  110.      * @var integer 
  111.      * @since 1.0
  112.      */
  113.     private $position 0;
  114.     
  115.     /**
  116.      * The height of the graph
  117.      * 
  118.      * @var integer 
  119.      * @since 1.0
  120.      */
  121.     private $height 0;
  122.     
  123.     /**
  124.      * The width of the graph
  125.      * 
  126.      * @var integer 
  127.      * @since 1.0
  128.      */
  129.     private $width 0;
  130.  
  131.     /**
  132.      * Trace logger
  133.      * 
  134.      * @var Logger 
  135.      * @since 1.0
  136.      */
  137.     private static $logger null;
  138.     
  139.     /**
  140.      * Constructor
  141.      * 
  142.      * @param integer $rowSpace 
  143.      * @param integer $colSpace 
  144.      * @param integer $branchSpace 
  145.      * @since 1.0
  146.      */
  147.     public function __construct($rowSpace 40 $colSpace 40$branchSpace 80{
  148.         self::$logger new Logger('AlphaTreeGraph');
  149.         
  150.         $this->root new AlphaGraphNode(000);
  151.         $this->rowSpace $rowSpace;
  152.         $this->colSpace $colSpace;
  153.         $this->branchSpace $branchSpace;
  154.     }
  155.  
  156.  
  157.     /**
  158.      * Add a new node to the graph
  159.      * 
  160.      * @param integer $id 
  161.      * @param integer $pid 
  162.      * @param string $message 
  163.      * @param integer $w 
  164.      * @param integer $h 
  165.      * @param string $nodeColour 
  166.      * @param string $URL 
  167.      * @since 1.0
  168.      */
  169.     public function add($id$pid$message ''$w 0$h 0$nodeColour$URL{
  170.         $node new AlphaGraphNode($id$w$h$message$nodeColour$URL);
  171.         
  172.         if(isset($this->nodes[$pid])) {
  173.             $pnode $this->nodes[$pid];
  174.             $node->setParentNode($pnode);
  175.             $pnode->addChild($node);
  176.         }else{
  177.             $pnode $this->root;
  178.             $node->setParentNode($pnode);
  179.             $this->root->addChild($node);        
  180.         }
  181.         
  182.         $this->nodes[$id$node;
  183.     }
  184.     
  185.     /**
  186.      * The first pass of the graph
  187.      * 
  188.      * @param AlphaGraphNode $node 
  189.      * @param integer $level 
  190.      * @since 1.0
  191.      */
  192.     private function firstPass($node$level{
  193.         $this->setNeighbours($node$level);
  194.         
  195.         if($node->childCount(== 0{
  196.             $leftSibling $node->getLeftSibling();
  197.             
  198.             if(isset($leftSibling)) {
  199.                 $node->setOffset($leftSibling->getOffset($leftSibling->getWidth($this->colSpace);
  200.             }else{
  201.                 $node->setOffset(0);
  202.             }
  203.         }else{
  204.             $childCount $node->childCount();
  205.             
  206.             for($i 0$i $childCount$i++{
  207.                 $this->firstPass($node->getChildAt($i)$level 1);
  208.             }
  209.  
  210.             $midPoint $node->getChildrenCenter();
  211.             $midPoint -= $node->getWidth()/2;
  212.             $leftSibling $node->getLeftSibling();
  213.             
  214.             if(isset($leftSibling)) {
  215.                 $node->setOffset($leftSibling->getOffset($leftSibling->getWidth($this->colSpace);
  216.                 $node->setModifier($node->getOffset($midPoint);
  217.  
  218.                 $this->layout($node$level);
  219.             }else{                
  220.                 $node->setOffset($midPoint);
  221.             }
  222.         }
  223.  
  224.         self::$logger->debug('Memory usage at first scan ['.((memory_get_usage(true)/1024)/1024).' MB]');
  225.     }
  226.     
  227.     /**
  228.      * The second pass of the graph
  229.      * 
  230.      * @param AlphaGraphNode $node 
  231.      * @param integer $level 
  232.      * @param integer $x 
  233.      * @param integer $y 
  234.      * @since 1.0
  235.      */
  236.     private function secondPass($node$level$x 0$y 0{
  237.         $nodeX $node->getOffset()+$x;
  238.         $nodeY $y;
  239.         
  240.         $node->setX($nodeX);
  241.         $node->setY($nodeY);
  242.         
  243.         $this->height ($this->height $node->getY($node->getWidth()) $this->height $node->getY($node->getWidth();
  244.         $this->width ($this->width $nodeX $node->getWidth()) $this->width $nodeX $node->getWidth()+10;
  245.  
  246.         if($node->childCount(0{
  247.             $this->secondPass($node->getChildAt(0)$level 1$x $node->getModifier()$y $node->getHeight($this->rowSpace);
  248.         }
  249.         
  250.         $rightSibling $node->getRightSibling();
  251.         
  252.         if(isset($rightSibling)) {
  253.             $this->secondPass($rightSibling$level$x$y);
  254.         }
  255.         
  256.         self::$logger->debug('Memory usage at second scan ['.((memory_get_usage(true)/1024)/1024).' MB]');
  257.     }
  258.     
  259.     /**
  260.      * Handles the laying out of multi-branch trees
  261.      * 
  262.      * @param AlphaGraphNode $node 
  263.      * @param integer $level 
  264.      * @since 1.0
  265.      */
  266.     private function layout($node$level{
  267.         
  268.         $firstChild $node->getChildAt(0);
  269.         $firstChildLeftNeighbour $firstChild->getLeftSibling();
  270.         
  271.         for($j 1$j <= $level$j++{
  272.             $modifierSumRight 0;
  273.             $modifierSumLeft 0;
  274.             $rightAncestor $firstChild;
  275.             $leftAncestor $firstChildLeftNeighbour;
  276.             
  277.             for($l 0$l $j$l++{
  278.                 $rightAncestor $rightAncestor->getParentNode();
  279.                 $leftAncestor $leftAncestor->getParentNode();
  280.                 $modifierSumRight += $rightAncestor->getModifier();
  281.                 $modifierSumLeft += $leftAncestor->getModifier();
  282.             }
  283.  
  284.             $totalGap ($firstChildLeftNeighbour->getOffset($modifierSumLeft $firstChildLeftNeighbour->getWidth($this->branchSpace($firstChild->getOffset($modifierSumRight);
  285.             
  286.             if($totalGap 0{
  287.                 $subTree $node;
  288.                 $subTreesCount 0;
  289.  
  290.                 while(isset($subTree&& $subTree !== $leftAncestor{
  291.                     $subTree $subTree->getLeftSibling();
  292.                     $subTreesCount++;
  293.                 }
  294.  
  295.                 $subTreeMove $node;
  296.                 $singleGap $totalGap $subTreesCount;
  297.                 
  298.                 while(isset($subTreeMove&& $subTreeMove !== $leftAncestor){
  299.                     $subTreeMove $subTreeMove->getLeftSibling();
  300.                     
  301.                     if(isset($subTreeMove)) {
  302.                         $subTreeMove->setOffset($subTreeMove->getOffset($totalGap);
  303.                         $subTreeMove->setModifier($subTreeMove->getModifier($totalGap);
  304.                         $totalGap -= $singleGap;
  305.                     }
  306.                 }
  307.             }
  308.             
  309.             if($firstChild->childCount(== 0{
  310.                 $firstChild $this->getLeftmost($node0$j);
  311.             }else{
  312.                 $firstChild $firstChild->getChildAt(0);
  313.             }
  314.             
  315.             if(isset($firstChild))    {
  316.                 $firstChildLeftNeighbour $firstChild->getLeftSibling();
  317.             }
  318.         }
  319.     }
  320.     
  321.     /**
  322.      * Setup neighbour nodes
  323.      * 
  324.      * @param AlphaGraphNode $node 
  325.      * @param integer $level 
  326.      * @since 1.0
  327.      */
  328.     private function setNeighbours($node$level{
  329.         if(isset($this->previousLevelNodes[$level]))
  330.             $node->setLeftSibling($this->previousLevelNodes[$level]);
  331.             
  332.         if($node->getLeftSibling())    {
  333.             $node->getLeftSibling()->setRightSibling($node);
  334.         }
  335.         $this->previousLevelNodes[$level$node;    
  336.     }
  337.     
  338.     /**
  339.      * Get left most node in the branch
  340.      * 
  341.      * @param AlphaGraphNode $node 
  342.      * @param integer $level 
  343.      * @param integer $maxlevel 
  344.      * @return AlphaGraphNode 
  345.      * @since 1.0
  346.      */
  347.     private function getLeftmost($node$level$maxlevel{
  348.         if($level >= $maxlevel)    {
  349.             return $node;
  350.         }
  351.         
  352.         $childCount $node->childCount();
  353.         
  354.         if($childCount == 0{
  355.             return null;
  356.         }
  357.         
  358.         for($i 0$i $childCount$i++{
  359.             $child $node->getChildAt($i);
  360.             
  361.             $leftmostDescendant $this->getLeftmost($child$level 1$maxlevel);
  362.             
  363.             if(isset($leftmostDescendant)) {
  364.                 return $leftmostDescendant;
  365.             }
  366.         }
  367.         
  368.         return null;
  369.     }
  370.     
  371.     /**
  372.      * Render the chart in memory
  373.      * 
  374.      * @since 1.0
  375.      */
  376.     protected function render()    {
  377.         $this->firstPass($this->root0);
  378.         $this->secondPass($this->root0);
  379.         
  380.         foreach($this->nodes as $node{
  381.             $node->setUpLinks();
  382.         }
  383.         
  384.         $this->isRendered true;
  385.     }
  386.     
  387.     /**
  388.      * Get the width of the graph, will invoke render() if not already rendered
  389.      * 
  390.      * @since 1.0
  391.      */
  392.     public function getWidth({
  393.         if(!$this->isRendered{
  394.             $this->render();
  395.         }
  396.         return $this->width;
  397.     }
  398.     
  399.     /**
  400.      * Get the heith of the graph, will invoke render() if not already rendered
  401.      * 
  402.      * @since 1.0
  403.      */
  404.     public function getHeight()    {
  405.         if(!$this->isRendered)
  406.             $this->render();
  407.  
  408.         return $this->height;
  409.     }    
  410.     
  411.     /**
  412.      * Get the next AlphaGraphNode instance in the graph, will invoke render() if not already rendered
  413.      * 
  414.      * @return AlphaGraphNode 
  415.      * @since 1.0
  416.      */
  417.     public function next({
  418.         if(!$this->isRendered{
  419.             $this->render();
  420.         }
  421.         
  422.         if(isset($this->nodes[$this->position+1])) {
  423.             $this->position++;
  424.             return $this->nodes[$this->position];
  425.         }else{
  426.             return null;
  427.         }
  428.     }
  429.  
  430.     /**
  431.      * Check to see if another AlphaGraphNode instance in the graph is available
  432.      * 
  433.      * @return boolean 
  434.      * @since 1.0
  435.      */
  436.     public function hasNext({
  437.         if(isset($this->nodes[$this->position+1]))
  438.             return true;
  439.         else        
  440.             return false;
  441.     }
  442. }
  443.  
  444. ?>

Documentation generated on Thu, 17 Mar 2011 16:43:40 +0000 by phpDocumentor 1.4.3