src/SixtyNine/Cloud/Usher/Usher.php
<?php
namespace SixtyNine\Cloud\Usher;
use Imagine\Image\Point;
use SixtyNine\Cloud\Drawer\Drawer;
use SixtyNine\Cloud\Factory\Logger;
use SixtyNine\Cloud\FontMetrics;
use SixtyNine\DataTypes\Box;
use SixtyNine\Cloud\Placer\PlacerInterface;
/**
* Responsible to find a place for the word in the cloud
*/
class Usher
{
const DEFAULT_MAX_TRIES = 100000;
/** @var int */
protected $maxTries;
/** @var \SixtyNine\Cloud\Usher\MaskInterface */
protected $mask;
/** @var \SixtyNine\Cloud\Placer\PlacerInterface */
protected $placer;
/** @var \SixtyNine\Cloud\FontMetrics */
protected $metrics;
/** @var int */
protected $precise;
/** @var Logger */
protected $logger;
/**
* @param int $imgWidth
* @param int $imgHeight
* @param PlacerInterface $placer
* @param FontMetrics $metrics
* @param bool $precise
* @param int $maxTries
*/
public function __construct(
$imgWidth,
$imgHeight,
PlacerInterface $placer,
FontMetrics $metrics,
$precise = false,
$maxTries = self::DEFAULT_MAX_TRIES
) {
$this->metrics = $metrics;
$this->imgHeight = $imgHeight;
$this->imgWidth = $imgWidth;
$this->maxTries = $maxTries;
$this->placer = $placer;
$this->logger = Logger::getInstance();
$this->precise = $precise;
$this->mask = $precise
? new PreciseMask($imgWidth, $imgHeight, $this->metrics)
: new QuadTreeMask($imgWidth, $imgHeight)
;
}
/**
* @param string $word
* @param string $font
* @param int $fontSize
* @param int $angle
* @return bool|Box
*/
public function getPlace($word, $font, $fontSize, $angle)
{
$this->logger->log(
sprintf(
'Search place for "%s", font = %s(%s), angle = %s',
$word,
str_replace('.ttf', '', $font),
$fontSize,
$angle
),
Logger::DEBUG
);
$bounds = new Box(0, 0, $this->imgWidth, $this->imgHeight);
$size = $this->metrics->calculateSize($word, $font, $fontSize);
$box = Drawer::getBoxForText(0, 0, $size->getWidth(), $size->getHeight(), $angle);
$this->logger->log(' Text dimensions: ' . $size->getDimensions(), Logger::DEBUG);
$place = $this->searchPlace($bounds, $box);
if (!$place) {
return false;
}
if ($this->precise) {
$this->mask->addWordToMask($word, $place, $font, $fontSize, $angle);
return $place;
}
$this->mask->add(new Point(0, 0), $place);
return $place;
}
/**
* Search a free place for a new box.
* @param \SixtyNine\DataTypes\Box $bounds
* @param \SixtyNine\DataTypes\Box $box
* @return bool|Box
*/
protected function searchPlace(Box $bounds, Box $box)
{
$this->logger->log(' Search place for ' . $box, Logger::DEBUG);
$placeFound = false;
$current = $this->placer->getFirstPlaceToTry();
$curTry = 1;
$currentBox = null;
while (!$placeFound) {
if (!$current) {
return false;
}
if ($curTry > $this->maxTries) {
return false;
}
$currentBox = $box->move($current->getX(), $current->getY());
$outOfBounds = !$currentBox->inside($bounds);
if (!$outOfBounds) {
$placeFound = !$this->mask->overlaps($currentBox);
$placeFound = $placeFound && !$outOfBounds;
}
$this->logger->log(sprintf(
' Trying %s --> %s',
$currentBox,
$outOfBounds ? 'Out of bounds' : ($placeFound ? 'OK' : 'Collision')
), Logger::DEBUG);
if ($placeFound) {
break;
}
$current = $this->placer->getNextPlaceToTry($current);
$curTry++;
}
return $currentBox->inside($bounds) ? $currentBox : false;
}
/**
* @return MaskInterface
*/
public function getMask()
{
return $this->mask;
}
}