Task: Ray-casting algorithm


C++: #include <algorithm>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <limits>

using namespace std;

const double epsilon = numeric_limits<float>().epsilon();
const numeric_limits<double> DOUBLE;
const double MIN = DOUBLE.min();
const double MAX = DOUBLE.max();

struct Point { const double x, y; };

struct Edge {
    const Point a, b;

    bool operator()(const Point& p) const
    {
        if (a.y > b.y) return Edge{ b, a }(p);
        if (p.y == a.y || p.y == b.y) return operator()({ p.x, p.y + epsilon });
        if (p.y > b.y || p.y < a.y || p.x > max(a.x, b.x)) return false;
        if (p.x < min(a.x, b.x)) return true;
        auto blue = abs(a.x - p.x) > MIN ? (p.y - a.y) / (p.x - a.x) : MAX;
        auto red = abs(a.x - b.x) > MIN ? (b.y - a.y) / (b.x - a.x) : MAX;
        return blue >= red;
    }
};

struct Figure {
    const string  name;
    const initializer_list<Edge> edges;

    bool contains(const Point& p) const
    {
        auto c = 0;
        for (auto e : edges) if (e(p)) c++;
        return c % 2 != 0;
    }

    template<unsigned char W = 3>
    void check(const initializer_list<Point>& points, ostream& os) const
    {
        os << "Is point inside figure " << name <<  '?' << endl;
        for (auto p : points)
            os << "  (" << setw(W) << p.x << ',' << setw(W) << p.y << "): " << boolalpha << contains(p) << endl;
        os << endl;
    }
};

int main()
{
    const initializer_list<Point> points =  { { 5.0, 5.0}, {5.0, 8.0}, {-10.0, 5.0}, {0.0, 5.0}, {10.0, 5.0}, {8.0, 5.0}, {10.0, 10.0} };
    const Figure square = { "Square",
        {  {{0.0, 0.0}, {10.0, 0.0}}, {{10.0, 0.0}, {10.0, 10.0}}, {{10.0, 10.0}, {0.0, 10.0}}, {{0.0, 10.0}, {0.0, 0.0}} }
    };

    const Figure square_hole = { "Square hole",
        {  {{0.0, 0.0}, {10.0, 0.0}}, {{10.0, 0.0}, {10.0, 10.0}}, {{10.0, 10.0}, {0.0, 10.0}}, {{0.0, 10.0}, {0.0, 0.0}},
           {{2.5, 2.5}, {7.5, 2.5}}, {{7.5, 2.5}, {7.5, 7.5}}, {{7.5, 7.5}, {2.5, 7.5}}, {{2.5, 7.5}, {2.5, 2.5}}
        }
    };

    const Figure strange = { "Strange",
        {  {{0.0, 0.0}, {2.5, 2.5}}, {{2.5, 2.5}, {0.0, 10.0}}, {{0.0, 10.0}, {2.5, 7.5}}, {{2.5, 7.5}, {7.5, 7.5}},
           {{7.5, 7.5}, {10.0, 10.0}}, {{10.0, 10.0}, {10.0, 0.0}}, {{10.0, 0}, {2.5, 2.5}}
        }
    };

    const Figure exagon = { "Exagon",
        {  {{3.0, 0.0}, {7.0, 0.0}}, {{7.0, 0.0}, {10.0, 5.0}}, {{10.0, 5.0}, {7.0, 10.0}}, {{7.0, 10.0}, {3.0, 10.0}},
           {{3.0, 10.0}, {0.0, 5.0}}, {{0.0, 5.0}, {3.0, 0.0}}
        }
    };

    for(auto f : {square, square_hole, strange, exagon})
        f.check(points, cout);

    return EXIT_SUCCESS;
}





PHP: <?php

function contains($bounds, $lat, $lng)
{
    $count = 0;
    $bounds_count = count($bounds);
    for ($b = 0; $b < $bounds_count; $b++) {
        $vertex1 = $bounds[$b];
        $vertex2 = $bounds[($b + 1) % $bounds_count];
        if (west($vertex1, $vertex2, $lng, $lat))
            $count++;
    }

    return $count % 2;
}

function west($A, $B, $x, $y)
{
    if ($A['y'] <= $B['y']) {
        if ($y <= $A['y'] || $y > $B['y'] ||
            $x >= $A['x'] && $x >= $B['x']) {
            return false;
        }
        if ($x < $A['x'] && $x < $B['x']) {
            return true;
        }
        if ($x == $A['x']) {
            if ($y == $A['y']) {
                $result1 = NAN;
            } else {
                $result1 = INF;
            }
        } else {
            $result1 = ($y - $A['y']) / ($x - $A['x']);
        }
        if ($B['x'] == $A['x']) {
            if ($B['y'] == $A['y']) {
                $result2 = NAN;
            } else {
                $result2 = INF;
            }
        } else {
            $result2 = ($B['y'] - $A['y']) / ($B['x'] - $A['x']);
        }
        return $result1 > $result2;
    }

    return west($B, $A, $x, $y);
}

$square = [
    'name' => 'square',
    'bounds' => [['x' => 0, 'y' => 0], ['x' => 20, 'y' => 0], ['x' => 20, 'y' => 20], ['x' => 0, 'y' => 20]]
];
$squareHole = [
    'name' => 'squareHole',
    'bounds' => [['x' => 0, 'y' => 0], ['x' => 20, 'y' => 0], ['x' => 20, 'y' => 20], ['x' => 0, 'y' => 20], ['x' => 5, 'y' => 5], ['x' => 15, 'y' => 5], ['x' => 15, 'y' => 15], ['x' => 5, 'y' => 15]]
];
$strange = [
    'name' => 'strange',
    'bounds' => [['x' => 0, 'y' => 0], ['x' => 5, 'y' => 5], ['x' => 0, 'y' => 20], ['x' => 5, 'y' => 15], ['x' => 15, 'y' => 15], ['x' => 20, 'y' => 20], ['x' => 20, 'y' => 0]]
];
$hexagon = [
    'name' => 'hexagon',
    'bounds' => [['x' => 6, 'y' => 0], ['x' => 14, 'y' => 0], ['x' => 20, 'y' => 10], ['x' => 14, 'y' => 20], ['x' => 6, 'y' => 20], ['x' => 0, 'y' => 10]]
];
 
$shapes = [$square, $squareHole, $strange, $hexagon];

$testPoints = [
    ['lng' => 10, 'lat' => 10],
    ['lng' => 10, 'lat' => 16],
    ['lng' => -20, 'lat' => 10],
    ['lng' => 0, 'lat' => 10],
    ['lng' => 20, 'lat' => 10],
    ['lng' => 16, 'lat' => 10],
    ['lng' => 20, 'lat' => 20]
];
 
for ($s = 0; $s < count($shapes); $s++) {
    $shape = $shapes[$s];
    for ($tp = 0; $tp < count($testPoints); $tp++) {
        $testPoint = $testPoints[$tp];
        echo json_encode($testPoint) . "\tin " . $shape['name'] . "\t" . contains($shape['bounds'], $testPoint['lat'], $testPoint['lng']) . PHP_EOL;
    }
}