src/Type/CubeMap/Mapping/Face.php
<?php
namespace AmaTeam\Image\Projection\Type\CubeMap\Mapping;
/**
* This class represents single face of a cube map and is used to
* convert UV coordinates to vector emerging from cube center and vice
* versa.
*
* Conventions used are common ones:
* - UV coordinates are set in [0, 1] range
* - Vector is a four-element array: [x, y, z, length]. xyz coordinates
* are set in [-1, 1] range, length depends on xyz values and lies in
* range 0..sqrt(3).
* - X and Z axis are located in 'horizontal' plane, Y is located in
* 'vertical' plane; X is directed towards R(ight) face, Y is
* directed towards U(p) face, Z is directed towards F(ront) face.
*
* @see https://docs.unity3d.com/uploads/Textures/CubeLayout6Faces.png
* @see http://www.3dcpptutorials.sk/obrazky/cube_map.jpg
* @see https://www.evl.uic.edu/aej/525/pics/cubemap-diagram.jpg
*/
class Face
{
const UP = 'u';
const LEFT = 'l';
const FRONT = 'f';
const RIGHT = 'r';
const BACK = 'b';
const DOWN = 'd';
/**
* y = +1
*/
const UP_DEFINITION = [
'name' => self::UP,
'pin' => [1, 1],
'u' => [0, 1],
'v' => [2, 1]
];
/**
* x = -1
*/
const LEFT_DEFINITION = [
'name' => self::LEFT,
'pin' => [0, -1],
'u' => [2, 1],
'v' => [1, -1],
];
/**
* z = +1
*/
const FRONT_DEFINITION = [
'name' => self::FRONT,
'pin' => [2, 1],
'u' => [0, 1],
'v' => [1, -1],
];
/**
* x = +1
*/
const RIGHT_DEFINITION = [
'name' => self::RIGHT,
'pin' => [0, 1],
'u' => [2, -1],
'v' => [1, -1]
];
/**
* z = -1
*/
const BACK_DEFINITION = [
'name' => self::BACK,
'pin' => [2, -1],
'u' => [0, -1],
'v' => [1, -1]
];
/**
* y = -1
*/
const DOWN_DEFINITION = [
'name' => self::DOWN,
'pin' => [1, -1],
'u' => [0, 1],
'v' => [2, -1]
];
/**
* @var string
*/
private $name;
/**
* @var int[]
*/
private $pin;
/**
* @var int[]
*/
private $uMapping;
/**
* @var int[]
*/
private $vMapping;
/**
* @param string $name
* @param int[] $pin Ordinal number and value of dimension being pinned
* @param int[] $uMapping Ordinal number and multiplier of U dimension
* @param int[] $vMapping Ordinal number and multiplier of V dimension
*/
public function __construct(
$name,
array $pin,
array $uMapping,
array $vMapping
) {
$this->name = $name;
$this->pin = $pin;
$this->uMapping = $uMapping;
$this->vMapping = $vMapping;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return string[]
*/
public static function getNames()
{
return [
self::RIGHT,
self::LEFT,
self::UP,
self::DOWN,
self::FRONT,
self::BACK,
];
}
/**
* Maps vector to UV coordinates in [0..1, 0..1] range
*
* @param float[] $vector
* @return float[]
*/
public function map(array $vector)
{
$max = max(abs($vector[0]), abs($vector[1]), abs($vector[2]));
$vector = Vector::multiply($vector, 1 / $max);
$u = 0.5 + ($vector[$this->uMapping[0]] * $this->uMapping[1] / 2);
$v = 0.5 + ($vector[$this->vMapping[0]] * $this->vMapping[1] / 2);
return [$u, $v];
}
/**
* Maps UV coordinates to vector in [-1..1 (x), (y), (z)] range
*
* @param float $u
* @param float $v
* @return float[]
*/
public function vectorize($u, $v)
{
$target = [];
$target[$this->uMapping[0]] = ($u - 0.5) * 2 * $this->uMapping[1];
$target[$this->vMapping[0]] = ($v - 0.5) * 2 * $this->vMapping[1];
$target[$this->pin[0]] = $this->pin[1];
// intentional unrolling
$squared = $target[0] * $target[0] + $target[1] * $target[1] +
$target[2] * $target[2];
$target[3] = sqrt($squared);
return $target;
}
public static function create($definition)
{
return new Face(
$definition['name'],
$definition['pin'],
$definition['u'],
$definition['v']
);
}
/**
* @return Face[]
*/
public static function generateCubeFaces()
{
return [
self::create(self::RIGHT_DEFINITION),
self::create(self::LEFT_DEFINITION),
self::create(self::UP_DEFINITION),
self::create(self::DOWN_DEFINITION),
self::create(self::FRONT_DEFINITION),
self::create(self::BACK_DEFINITION),
];
}
}