sixty-nine/php-cloud-lib

View on GitHub
src/SixtyNine/Cloud/Usher/Usher.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?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;
    }
}