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