<?php
namespace Mindee\Geometry;
use Mindee\Error\MindeeGeometryException;
/**
* Utility class for Polygon.
*/
abstract class PolygonUtils
{
/**
* Gets the centroid (Point) of a set of points.
*
* @param array $vertices Array of points.
* @return Point
*/
public static function getCentroid(array $vertices): Point
{
$verticesSum = count($vertices);
$xSum = 0.0;
$ySum = 0.0;
foreach ($vertices as $vertex) {
/* @var Point $vertex */
$xSum += $vertex->getX();
$ySum += $vertex->getY();
}
return new Point($xSum / $verticesSum, $ySum / $verticesSum);
}
/**
* Retrieves the minimum y coordinate of a Polygon.
*
* @param Polygon $polygon Polygon to get the minimum y coordinate of.
* @return float
* @throws MindeeGeometryException Throws if a minimum y-axis value cannot
* be found, e.g. if the polygon is empty.
*/
public static function getMinYCoordinate(Polygon $polygon): float
{
$min = null;
foreach ($polygon->getCoordinates() as $point) {
if (!isset($min) || $min > $point->getY()) {
$min = $point->getY();
}
}
if (!isset($min)) {
throw new MindeeGeometryException(
'The provided polygon seems to be empty, or the Y coordinates of each point are invalid.'
);
}
return $min;
}
/**
* Retrieves the minimum x coordinate of a Polygon.
*
* @param Polygon $polygon Polygon to get the minimum y coordinate of.
* @return float
* @throws MindeeGeometryException Throws if a minimum x-axis value cannot be
* found, e.g. if the polygon is empty.
*/
public static function getMinXCoordinate(Polygon $polygon): float
{
$min = null;
foreach ($polygon->getCoordinates() as $point) {
if (!isset($min) || $min > $point->getX()) {
$min = $point->getX();
}
}
if (!isset($min)) {
throw new MindeeGeometryException(
'The provided polygon seems to be empty, or the X coordinates of each point are invalid.'
);
}
return $min;
}
/**
* Retrieves the maximum y coordinate of a Polygon.
*
* @param Polygon $polygon Polygon to get the minimum y coordinate of.
* @return float
* @throws MindeeGeometryException Throws if a maximum y-axis value cannot be
* found, e.g. if the polygon is empty.
*/
public static function getMaxYCoordinate(Polygon $polygon): float
{
$min = null;
foreach ($polygon->getCoordinates() as $point) {
if (!isset($min) || $min < $point->getY()) {
$min = $point->getY();
}
}
if (!isset($min)) {
throw new MindeeGeometryException(
'The provided polygon seems to be empty, or the Y coordinates of each point are invalid.'
);
}
return $min;
}
/**
* Retrieves the maximum x coordinate of a Polygon.
*
* @param Polygon $polygon Polygon to get the minimum y coordinate of.
* @return float
* @throws MindeeGeometryException Throws if a maximum x-axis value cannot be
* found, e.g. if the polygon is empty.
*/
public static function getMaxXCoordinate(Polygon $polygon): float
{
$min = null;
foreach ($polygon->getCoordinates() as $point) {
if (!isset($min) || $min < $point->getX()) {
$min = $point->getX();
}
}
if (!isset($min)) {
throw new MindeeGeometryException(
'The provided polygon seems to be empty, or the X coordinates of each point are invalid.'
);
}
return $min;
}
/**
* Compares two polygons on the Y axis. Returns a sort-compliant result (0;-1;1).
*
* @param Polygon $polygon1 First polygon to compare.
* @param Polygon $polygon2 Second polygon to compare.
* @return integer
*/
public static function compareOnY(Polygon $polygon1, Polygon $polygon2): int
{
$sort = self::getMinYCoordinate($polygon1) - self::getMinYCoordinate($polygon2);
if ($sort == 0) {
return 0;
}
return $sort < 0 ? -1 : 1;
}
/**
* Merges two polygons.
*
* @param Polygon|null $base First polygon to merge.
* @param Polygon|null $target Second polygon to merge.
* @return Polygon
* @throws MindeeGeometryException Throws if both polygons are empty.
*/
public static function merge(?Polygon $base, ?Polygon $target): Polygon
{
if ((!$base || !$base->getCoordinates()) && (!$target || !$target->getCoordinates())) {
throw new MindeeGeometryException('Cannot merge two empty polygons.');
}
if (!$base || !$base->getCoordinates()) {
return $target;
}
if (!$target || !$target->getCoordinates()) {
return $base;
}
return new Polygon(array_unique(array_merge($base->getCoordinates(), $target->getCoordinates()), SORT_REGULAR));
}
/**
* Creates a bounding box from one or two polygons.
*
* @param Polygon|null $base First polygon.
* @param Polygon|null $target Second polygon.
* @return Polygon
*/
public static function createBoundingBoxFrom(?Polygon $base, ?Polygon $target = null): Polygon
{
$merged = PolygonUtils::merge($base, $target);
$topLeft = new Point(self::getMinXCoordinate($merged), self::getMinYCoordinate($merged));
$topRight = new Point(self::getMaxXCoordinate($merged), self::getMinYCoordinate($merged));
$bottomRight = new Point(self::getMaxXCoordinate($merged), self::getMaxYCoordinate($merged));
$bottomLeft = new Point(self::getMinXCoordinate($merged), self::getMaxYCoordinate($merged));
return new Polygon([
$topLeft,
$topRight,
$bottomRight,
$bottomLeft,
]);
}
/**
* Generates a quadrilateral Polygon from a given prediction.
*
* @param array $prediction Raw prediction array.
* @return Polygon
* @throws MindeeGeometryException Throws if the polygon isn't a quadrilateral.
*/
public static function quadrilateralFromPrediction(array $prediction): Polygon
{
if (count($prediction) != 4) {
throw new MindeeGeometryException('Prediction must have exactly 4 points.');
}
return new Polygon([
new Point($prediction[0][0], $prediction[0][1]),
new Point($prediction[1][0], $prediction[1][1]),
new Point($prediction[2][0], $prediction[2][1]),
new Point($prediction[3][0], $prediction[3][1]),
]);
}
/**
* Generates a Polygon from a given prediction.
*
* @param array $prediction Raw prediction array.
* @return Polygon
*/
public static function polygonFromPrediction(array $prediction): Polygon
{
$points = [];
foreach ($prediction as $point) {
$points[] = new Point($point[0], $point[1]);
}
return new Polygon($points);
}
/**
* Checks whether a point is located within a coordinate range on the x-axis.
*
* @param Point $point Point to check.
* @param float $minX Lower bound.
* @param float $maxX Upper bound.
* @return boolean
*/
public static function isPointInX(Point $point, float $minX, float $maxX): bool
{
return $point->getX() >= $minX && $point->getX() <= $maxX;
}
/**
* Checks whether a point is in a polygon's x-axis range.
*
* @param Point $point Point to check.
* @param Polygon $polygon Polygon.
* @return boolean
*/
public static function isPointInPolygonX(Point $point, Polygon $polygon): bool
{
$minX = MinMaxUtils::getMinMaxX($polygon->getCoordinates())->getMin();
$maxX = MinMaxUtils::getMinMaxX($polygon->getCoordinates())->getMax();
return self::isPointInX($point, $minX, $maxX);
}
/**
* Checks whether a point is located within a coordinate range on the y-axis.
*
* @param Point $point Point to check.
* @param float $minY Lower bound.
* @param float $maxY Upper bound.
* @return boolean
*/
public static function isPointInY(Point $point, float $minY, float $maxY): bool
{
return $point->getY() >= $minY && $point->getY() <= $maxY;
}
/**
* Checks whether a point is in a polygon's y-axis range.
*
* @param Point $point Point to check.
* @param Polygon $polygon Polygon.
* @return boolean
*/
public static function isPointInPolygonY(Point $point, Polygon $polygon): bool
{
$minY = MinMaxUtils::getMinMaxY($polygon->getCoordinates())->getMin();
$maxY = MinMaxUtils::getMinMaxY($polygon->getCoordinates())->getMax();
return self::isPointInY($point, $minY, $maxY);
}
}
|