Code Search for Developers
 
 
  

Hex.php from Astrum Futura at Krugle


Show Hex.php syntax highlighted

<?php
/**
 * @internal
 * Quantum Game Library
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the text file LICENSE located in the root
 * directory of this library.
 * It is also available through the internet at this URL:
 * http://doc.astrumfutura.com/license.html
 *
 * If you did not receive a copy of the license and are unable to
 * obtain it through the internet, please send an email
 * to license@astrumfutura.com so we can send you a copy.
 *
 * @package    Map
 * @subpackage Measure
 * @category   Quantum
 * @copyright  Copyright (c) 2007 The QGL Group (refer to COPYRIGHT file)
 * @version    $Id: Hex.php 210 2007-02-03 02:55:49Z santosj $
 * @license    http://doc.astrumfutura.com/license.html     New BSD License
 */

/** Quantum_Map_Measure_Abstract */
require_once('Quantum/Map/Measure/Abstract.php');

/**
 * Provides methods for measuring certain unit based measures of a Coordinate
 * Grid in following the Hex Map layout as specified for the Astrum Futura 
 * project. The approach we use is deliberately simple. I've seen too much
 * complicated hex coordinate math in researching for this class!
 *
 * Hex shape is assumed pointy-side up, i.e. cells with same x coordinate are in
 * the same horizontal row.
 *
 * @package    Map
 * @subpackage Measure
 * @category   Quantum
 * @author     Pádraic Brady (http://blog.astrumfutura.com)
 * @uses       Quantum_Map_Measure_Abstract
 */
class Quantum_Map_Measure_Hex extends Quantum_Map_Measure_Abstract
{

    /**
     * Constants all refer to the unit difference between the coordinates of
     * the current cell to a cell in the given direction (allowing us to
     * figure out their coordinates easily). Directions are given in degrees
     * starting from the West (0) in a clockwise direction to South-West (315)
     */

    /** West **/
    const DEG_0_X = -1;
    const DEG_0_Y = 0;
	/** North-West **/
	const DEG_45_X  = -1;
	const DEG_45_Y  = 1;
	/** North does not exist for Hex Maps (in a "pointy-vertex up" layout) **/
	/** North-East **/
	const DEG_135_X = 0;
	const DEG_135_Y = 1;
	/** East **/
	const DEG_180_X = 1;
	const DEG_180_Y = 0;
	/** South-East **/
	const DEG_225_X = 1;
	const DEG_225_Y = -1;
	/** South does not exist for Hex Maps (in a "pointy-vertex up" layout)  **/
	/** South-West **/
	const DEG_315_X = 0;
	const DEG_315_Y = -1;

    /*
     * Stores an instance of this class when instantiated.
     *
     * @access    private
     * @var       Quantum_Map_Measure_Hex
     */
    protected static $_instance = null;

    /**
     * Singleton static method for retrieving an instance of the DataAccess class.
     *
     * @return   Quantum_Map_Measure_hex
     * @access   public
     */
    static public function getInstance()
    {
        if(is_null(self::$_instance))
        {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    /**
     * Returns the minimum number of cells an entity must pass through to travel
     * on the map from a starting cell to a finish cell adhering to the map layout.
     * This differs from getUnitDistance which returns the absolute metric distance
     * (i.e. a measurement of distance "as the crow flies").
     *
     * @param   $startNode  Quantum_Coordinate_Interface
     * @param   $endNode  Quantum_Coordinate_Interface
     * @return  integer
     * @access  public
     */
    public function getMoveDistance(Quantum_Coordinate_Interface $startNode,
        Quantum_Coordinate_Interface $endNode)
    {
        /*
         * Logic of Moves changes depending on
         * relative x,y differences. The main decision
         * is determining which cell should be A or B
         * (i.e. startNode and endNode in _unitDistance)
         */
        if($endNode['x'] >= $startNode['x'])
        {
            return $this->_moveDistance($startNode, $endNode);
        }
        else // reverse use order of A and B
        {
            return $this->_moveDistance($endNode, $startNode);
        }
    }

    /**
     * Returns the absolute metric distance between the starting and end cells
     * (i.e. a measurement of distance "as the crow flies"). This returns a float
     * being an exact measure, and ignores any movement rules.
     *
     * @param   $startNode  Quantum_Coordinate
     * @param   $endNode  Quantum_Coordinate
     * @return  float
     * @access  public
     */
    public function getUnitDistance(Quantum_Coordinate_Interface $startNode,
        Quantum_Coordinate_Interface $endNode)
    {	
		/*
		 * Set distance to 0, the following is for clarity and some speed boost.
		 */
        $distance = 0;
        $dX = $endNode['x'] - $startNode['x'];
        $dY = $endNode['y'] - $startNode['y'];
        $distance = sqrt(   
             ($dX * $dX) + ($dY * $dY) + ($dX * $dY)
        );
        return (float) $distance;
    }

    /**
     * Return an array of neighbouring cells with short form direction strings
     * as keys. Order of return is clockwise from North-East (ne) to North-West
     * (nw). Non supported directions should be checked for NULL values as not
     * set. For Hex maps, for example, we use the "pointy-side up" orientation
     * meaning any Entities cannot move directly North or South on a map, only
     * in the other six compass directions.
     *
     * @param   $node  Quantum_Coordinate
     * @return  array
     * @access  public
     */
    public function getPerimeterCells(Quantum_Coordinate_Interface $node)
    {
        $cells = array();
        $cells['ne'] = array(
            'x'=>$node['x'] + self::DEG_135_X,
            'y'=>$node['y'] + self::DEG_135_Y
        );
        $cells['e'] = array(
            'x'=>$node['x'] + self::DEG_180_X,
            'y'=>$node['y'] + self::DEG_180_Y
        );
        $cells['se'] = array(
            'x'=>$node['x'] + self::DEG_225_X,
            'y'=>$node['y'] + self::DEG_225_Y
        );
        $cells['sw'] = array(
            'x'=>$node['x'] + self::DEG_315_X,
            'y'=>$node['y'] + self::DEG_315_Y
        );
        $cells['w'] = array(
            'x'=>$node['x'] + self::DEG_0_X,
            'y'=>$node['y'] + self::DEG_0_Y
        );
        $cells['nw'] = array(
            'x'=>$node['x'] + self::DEG_45_X,
            'y'=>$node['y'] + self::DEG_45_Y
        );
        return $cells;
    }

    /**
     * Returns the number of cells in any perimeter ring at any radius distance
     * from a center cell. This is actually a very simple calculation since for
     * Hex based shapes (6 sided polygon), if you fit all Hexes in a map, the
     * number of cells in any perimeter ring (or "orbit") always equals 6 times
     * the radius from the center cell. The same works for Square cell layouts
     * if you substitute 6 with 4 ;).
     *
     * @param   $radius  Radius distance from center cell
     * @return  integer
     * @access  public
     */
    public function getPerimeterFromRadius($radius)
    {
        if(!isset($radius) || !is_int($radius) || $radius < 0)
        {
            throw new Quantum_Map_Measure_Exception('Radius must be a whole number integer with a minimum value of 0.');
        }
        $perimeter = $radius * 6;
        if($perimeter == 0) // handle case where radius is 0, i.e. one cell, so area of 1
        {
            return 1;
        }
        return $perimeter;
    }

    /**
     * Calculated the area if any region on a hex map with the given radius (i.e
     * the region is a circular plane with a central cell.
     *
     * Area factor is the "Sum of the Series" where the series is a progression
     * reducing radius by 1 per iteration (e.g. Series of 6 is 6,5,4,3,2,1,0)
     * to zero.
     *
     * The Sum of Radii is then multiplied by the Hex perimeter count factor, 6.
     * We add 1, to represent the center cell and include it.
     *
     * @param   integer  $radius
     * @return  integer
     * @access  public
     */
    public function getAreaFromRadius($radius)
    {
        if(!isset($radius) || !is_int($radius) || $radius < 0)
        {
            throw new Quantum_Map_Measure_Exception('Radius must be an integer with a minimum value of 0.');
        }
        $sum_of_radii = 0;
        do
        {
            $sum_of_radii += $radius;
            $radius--;
        }
        while($radius > 0);
        // area of all rings outside center cell
        $area = $sum_of_radii * 6;
        // add center cell
        $area += 1;
        return $area;
    }

    /**
     * Basic move distance logic for Hex maps. The parent getMoveDistance()
     * method proxies to this one when it has determined the exact order of
     * coordinates to use (depends on which node's X coordinate is largest).
     *
     * @param   $startNode  Quantum_Coordinate_Interface
     * @param   $endNode  Quantum_Coordinate_Interface
     * @see     getMoveDistance
     * @return  integer
     * @access  private
     */
    private function _moveDistance(Quantum_Coordinate_Interface $A,
        Quantum_Coordinate_Interface $B)
    {
        if($B['y'] > $A['y'])
        {
           $dist = $B['x'] - $A['x'] + $B['y'] - $A['y'];
        }
        elseif(array_sum($A->getArray()) > array_sum($B->getArray()))
        {
           $dist = $A['y'] - $B['y'];
        }
        else
        {
            $dist = $B['x'] - $A['x'];
        }
        return $dist;
    }

}



See more files for this project here

Astrum Futura

Multiplayer space strategy game written in PHP5 with the Zend Framework. User interface uses Javascript/AJAX for dynamic interaction. Players compete across a hexagonal map of 10,000 sectors, planets, stars and other locations through trade and combat.

Project homepage: http://sourceforge.net/projects/astrumfutura
Programming language(s): PHP,XML
License: other

  Abstract.php
  Exception.php
  Hex.php
  Square.php