<?php
interface MatrixInterface
{
public function get($row, $column);
public function set($row, $column, $value);
public function getColumnCount();
public function getRowCount();
public function multiply(MatrixInterface $matrix);
}
class MatrixFactory
{
public function createTranslationMatrix($dx, $dy)
{
$m = new Matrix(3, 3);
$m->set(0, 0, 1); $m->set(0, 1, 0); $m->set(0, 2, $dx);
$m->set(1, 0, 0); $m->set(1, 1, 1); $m->set(1, 2, $dy);
$m->set(2, 0, 0); $m->set(2, 1, 0); $m->set(2, 2, 1);
return $m;
}
public function createRotationMatrix($radiant, Vector2D $rotationCenter)
{
$m = $this->createTranslationMatrix($rotationCenter->getX(), $rotationCenter->getY());
$n = new Matrix(3, 3);
$n->set(0, 0, cos($radiant));
$n->set(0, 1, -sin($radiant));
$n->set(0, 2, 0);
$n->set(1, 0, sin($radiant));
$n->set(1, 1, cos($radiant));
$n->set(1, 2, 0);
$n->set(2, 0, 0);
$n->set(2, 1, 0);
$n->set(2, 2, 1);
$o = $this->createTranslationMatrix(-$rotationCenter->getX(), -$rotationCenter->getY());
return $m->multiply($n)->multiply($o);
}
}
class Matrix implements MatrixInterface
{
protected $rows;
protected $columns;
protected $data;
public function __construct($rows, $columns)
{
$this->rows = $rows;
$this->columns = $columns;
$this->data = array_fill(0, $rows, array_fill(0, $columns, 0));
}
public function set($row, $column, $value)
{
$this->data[$row][$column] = $value;
}
public function get($row, $column)
{
if (!isset($this->data[$row][$column])) {
throw new Exception();
}
return $this->data[$row][$column];
}
public function getRowCount()
{
return $this->rows;
}
public function getColumnCount()
{
return $this->columns;
}
public function multiply(MatrixInterface $matrix)
{
if ($this->columns !== $matrix->getRowCount()) {
throw new Exception();
}
$rows = $matrix->getRowCount();
$columns = $matrix->getColumnCount();
$resultMatrix = new Matrix($rows, $columns);
for ($row = 0; $row < $rows; $row++) {
for ($column = 0; $column < $columns; $column++) {
$sum = 0;
for ($k = 0; $k < $rows; $k++) {
$sum += $this->get($row, $k) * $matrix->get($k, $column);
}
$resultMatrix->set($row, $column, $sum);
}
}
return $resultMatrix;
}
}
class Vector2D
{
protected $x;
protected $y;
public function __construct($x, $y)
{
$this->x = $x;
$this->y = $y;
}
public function getX()
{
return $this->x;
}
public function setX($x)
{
$this->x = $x;
}
public function getY()
{
return $this->y;
}
public function setY($y)
{
$this->y = $y;
}
public function transform(MatrixInterface $matrix)
{
$res = $matrix->multiply($this->convertToMatrix());
$this->x = $res->get(0, 0);
$this->y = $res->get(1, 0);
}
public function convertToMatrix()
{
$matrix = new Matrix(3, 1);
$matrix->set(0, 0, $this->x);
$matrix->set(1, 0, $this->y);
$matrix->set(2, 0, 1);
return $matrix;
}
}
class TextDrawer
{
protected $matrixFactory;
public function __construct(MatrixFactory $matrixFactory, Imagick $img)
{
$this->matrixFactory = $matrixFactory;
$this->img = $img;
}
protected function cn_PnPoly(Vector2D $P, array $V)
{
$n = count($V) - 1;
$cn = 0;
for ($i=0; $i<$n; $i++) {
if ((($V[$i]->getY() <= $P->getY()) && ($V[$i+1]->getY() > $P->getY()))
|| (($V[$i]->getY() > $P->getY()) && ($V[$i+1]->getY() <= $P->getY()))) {
$vt = (float)($P->getY() - $V[$i]->getY()) / ($V[$i+1]->getY() - $V[$i]->getY());
if ($P->getX() < $V[$i]->getX() + $vt * ($V[$i+1]->getX() - $V[$i]->getX()))
++$cn;
}
}
return ($cn&1);
}
protected function findLineIntersection(Vector2D $start1, Vector2D $end1, Vector2D $start2, Vector2D $end2)
{
$denom = (($end1->getX() - $start1->getX()) * ($end2->getY() - $start2->getY())) - (($end1->getY() - $start1->getY()) * ($end2->getX() - $start2->getX()));
if ($denom == 0) {
return null;
}
$numer = (($start1->getY() - $start2->getY()) * ($end2->getX() - $start2->getX())) - (($start1->getX() - $start2->getX()) * ($end2->getY() - $start2->getY()));
$r = $numer / $denom;
$numer2 = (($start1->getY() - $start2->getY()) * ($end1->getX() - $start1->getX())) - (($start1->getX() - $start2->getX()) * ($end1->getY() - $start1->getY()));
$s = $numer2 / $denom;
if (($r < 0 || $r > 1) || ($s < 0 || $s > 1)) {
return null;
}
$result = new Vector2D(0, 0);
$result->setX($start1->getX() + ($r * ($end1->getX() - $start1->getX())));
$result->setY($start1->getY() + ($r * ($end1->getY() - $start1->getY())));
return $result;
}
protected function bigBoundingBox(array $points = array())
{
$x_min = null;
$y_min = null;
$x_max = null;
$y_max = null;
foreach ($points as $point) {
$x = $point->getX();
$y = $point->getY();
if ($x_min === null || $x < $x_min) {
$x_min = $x;
}
if ($x_max === null || $x > $x_max) {
$x_max = $x;
}
if ($y_min === null || $y < $y_min) {
$y_min = $y;
}
if ($y_max === null || $y > $y_max) {
$y_max = $y;
}
}
return array($x_min, $y_min, $x_max, $y_max);
}
private function macgyverSomeStuff(ImagickDraw $draw, $text, $angleRange, $fontRange)
{
$fontSize = rand($fontRange[0], $fontRange[1]);
$x = rand(0, $this->img->getImageWidth());
$y = rand(0, $this->img->getImageHeight());
$rotation = mt_rand($angleRange[0], $angleRange[1]);
$draw->setFontSize($fontSize);
$fontMetrics = $this->img->queryFontMetrics($draw, $text);
$m = $this->matrixFactory->createRotationMatrix(deg2rad($rotation), new Vector2D($x, $y));
$a = new Vector2D($x, $y);
$b = new Vector2D($x + $fontMetrics['textWidth'], $y);
$c = new Vector2D($x + $fontMetrics['textWidth'], $y + $fontMetrics['textHeight']);
$d = new Vector2D($x, $y + $fontMetrics['textHeight']);
$a->transform($m);
$b->transform($m);
$c->transform($m);
$d->transform($m);
list($x0, $y0, $x1, $y1) = $this->bigBoundingBox(array($a, $b, $c, $d));
$dx = 0.0;
$dy = 0.0;
if ($x0 < 0) {
$dx = -$x0;
}
if ($y0 < 0) {
$dy = -$y0;
}
if ($x1 > $this->img->getImageWidth()) {
$dx = $this->img->getImageWidth() - $x1;
}
if ($y1 > $this->img->getImageHeight()) {
$dy = $this->img->getImageHeight() - $y1;
}
$n = $this->matrixFactory->createTranslationMatrix($dx, $dy);
$a->transform($n);
$b->transform($n);
$c->transform($n);
$d->transform($n);
$x += $dx;
$y += $dy;
return array($x, $y, $fontSize, $rotation, array($a, $b, $c, $d, $a));
}
protected function isOverlap($polygons, $test)
{
foreach ($polygons as $poly) {
foreach ($test as $point) {
if ($this->cn_PnPoly($point, $poly)) {
return true;
}
}
foreach ($poly as $point) {
if ($this->cn_PnPoly($point, $test)) {
return true;
}
}
}
foreach ($polygons as $polygon) {
$c_poly = count($polygon);
$c_test = count($test);
for ($i = 0; $i < $c_poly - 1; $i++) {
for ($j = 0; $j < $c_test - 1; $j++) {
$res = $this->findLineIntersection($polygon[$i], $polygon[$i+1], $test[$j], $test[$j+1]);
if ($res !== null) {
return true;
}
}
}
}
return false;
}
public function positionTexts($data, array $angleRange, array $fontRange)
{
$draw = new ImagickDraw();
$draw->setGravity(Imagick::GRAVITY_NORTHWEST);
$polygons = array();
$polygonCounter = 0;
foreach ($data as $entry) {
do {
list($x, $y, $fontSize, $rotation, $polygon)
= $this->macgyverSomeStuff($draw, $entry, $angleRange, $fontRange);
$polygonCounter++;
} while ($this->isOverlap($polygons, $polygon));
$draw->setStrokeColor(new ImagickPixel('green'));
$draw->setFillOpacity(0.0);
$c = count($polygon);
for ($i = 0; $i < $c - 1; $i++) {
$draw->line(
$polygon[$i]->getX(), $polygon[$i]->getY(),
$polygon[$i + 1]->getX(), $polygon[$i + 1]->getY()
);
}
$polygons[] = $polygon;
$draw->setStrokeColor('black');
$draw->setFontSize($fontSize);
$draw->setFillColor('black');
$draw->setFillOpacity(1.0);
$draw->setStrokeOpacity(1.0);
$this->img->annotateImage($draw, $x, $y, $rotation, $entry);
}
$draw->setFontSize(10);
$draw->setStrokeOpacity(0.0);
$draw->annotation(0, 0, 'Verworfene Polygone: ' . ($polygonCounter - count($data)));
$this->img->setImageFormat('jpg');
$this->img->drawimage($draw);
}
}
error_reporting(-1);
ini_set('display_errors', 1);
$texts = array(
'Borussia Dortmund',
'FC Bayern München',
'Hamburger SV',
'FC Schalke 04',
'FC Augsburg',
'VfB Stuttgart',
'Werder Bremen',
'Hannover 96',
'Bayer Leverkusen'
);
$img = new Imagick();
$img->newImage(640, 480, new ImagickPixel('white'));
$textDrawer = new TextDrawer(new MatrixFactory(), $img);
$textDrawer->positionTexts($texts, array(-50, 50), array(30, 60));
header('Content-Type: image/jpeg');
echo $img;