Utility classes

2019
class GridCircle
{
    public function __construct($center, $radius)
    {
        $this->center = xy($center);
        $this->radius = $radius;
    }

    /**
     * Get all the points in the area
     *
     * @return Set
     */
    public function getPoints()
    {
        $points = [];

        for ($x = $this->center[0] - $this->radius; $x <= $this->center[0] + $this->radius; $x++) {
            for ($y = $this->center[1] - $this->radius; $y <= $this->center[1] + $this->radius; $y++) {
                if (xy([$x, $y])->manhattan($this->center) <= $this->radius) {
                    $points[] = xy([$x, $y]);
                }
            }
        }
        return $points;
    }

    /**
     * Get the corners of the area (top, left, bottom, right)
     *
     * @return Set
     */
    public function getCorners()
    {
        return set([
            "left"   => xy([$this->center[0] - ($this->radius), $this->center[1]]),
            "right"  => xy([$this->center[0] + ($this->radius), $this->center[1]]),
            "top"    => xy([$this->center[0], $this->center[1] - ($this->radius)]),
            "bottom" => xy([$this->center[0], $this->center[1] + ($this->radius)])
        ]);
    }

    /**
     * Check if the area contains a point
     *
     * @param GridPointer $xy
     * @return bool
     */
    public function contains($xy)
    {
        return xy($xy)->manhattan($this->center) <= $this->radius;
    }

    /**
     * Find the intersection points of two areas
     *
     * @param GirdArea $area
     * @return Set of intersections points
     */
    public function getIntersections($area)
    {
        $a = $this->getCorners();
        $b = $area->getCorners();

        return set([
            static::getEdgeIntersection([$a["top"],    $a["right"]],  [$b["top"],    $b["left"]]),
            static::getEdgeIntersection([$b["top"],    $b["right"]],  [$a["top"],    $a["left"]]),
            static::getEdgeIntersection([$a["top"],    $a["right"]],  [$b["right"],  $b["bottom"]]),
            static::getEdgeIntersection([$b["top"],    $b["right"]],  [$a["right"],  $a["bottom"]]),
            static::getEdgeIntersection([$a["left"],   $a["bottom"]], [$b["top"],    $b["left"]]),
            static::getEdgeIntersection([$b["left"],   $b["bottom"]], [$a["top"] ,   $a["left"]]),
            static::getEdgeIntersection([$a["left"],   $a["bottom"]], [$b["right"],  $b["bottom"]]),
            static::getEdgeIntersection([$b["left"],   $b["bottom"]], [$a["right"],  $a["bottom"]]),
        ])->filter();
    }

    /**
     * Get the point of intersection between two edges
     *
     * @param array $edge_a
     * @param array $edge_b
     * @return GridPointer
     */
    public static function getEdgeIntersection($edge_a, $edge_b)
    {
        $x1  = $edge_a[0][0] < $edge_a[1][0] ? $edge_a[0][0] - $edge_a[0][1]: $edge_a[0][0] + $edge_a[0][1];
        $x2  = $edge_b[0][0] < $edge_b[1][0] ? $edge_b[0][0] - $edge_b[0][1]: $edge_b[0][0] + $edge_b[0][1];

        $w   = abs($x1 - $x2) / 2;
        $xy1 = [$x1 + $w, $w];
        $xy2 = [$x2 - $w, $w];

        // They do not meet at exatly the same point
        if ($xy1[0] != $xy2[0]) return false;

        // Outside of the area
        if ($edge_a[1][1] < $w || $edge_b[1][1] < $w) return false;
        if ($edge_a[0][1] > $w || $edge_b[0][1] > $w) return false;

        return xy($xy1);
    }

}