Task: Ray-casting algorithm




Go: package main

import (
    "fmt"
    "math"
)

type xy struct {
    x, y float64
}

type seg struct {
    p1, p2 xy
}

type poly struct {
    name  string
    sides []seg
}

func inside(pt xy, pg poly) (i bool) {
    for _, side := range pg.sides {
        if rayIntersectsSegment(pt, side) {
            i = !i
        }
    }
    return
}

func rayIntersectsSegment(p xy, s seg) bool {
    var a, b xy
    if s.p1.y < s.p2.y {
        a, b = s.p1, s.p2
    } else {
        a, b = s.p2, s.p1
    }
    for p.y == a.y || p.y == b.y {
        p.y = math.Nextafter(p.y, math.Inf(1))
    }
    if p.y < a.y || p.y > b.y {
        return false
    }
    if a.x > b.x {
        if p.x > a.x {
            return false
        }
        if p.x < b.x {
            return true
        }
    } else {
        if p.x > b.x {
            return false
        }
        if p.x < a.x {
            return true
        }
    }
    return (p.y-a.y)/(p.x-a.x) >= (b.y-a.y)/(b.x-a.x)
}

var (
    p1  = xy{0, 0}
    p2  = xy{10, 0}
    p3  = xy{10, 10}
    p4  = xy{0, 10}
    p5  = xy{2.5, 2.5}
    p6  = xy{7.5, 2.5}
    p7  = xy{7.5, 7.5}
    p8  = xy{2.5, 7.5}
    p9  = xy{0, 5}
    p10 = xy{10, 5}
    p11 = xy{3, 0}
    p12 = xy{7, 0}
    p13 = xy{7, 10}
    p14 = xy{3, 10}
)

var tpg = []poly{
    {"square", []seg{{p1, p2}, {p2, p3}, {p3, p4}, {p4, p1}}},
    {"square hole", []seg{{p1, p2}, {p2, p3}, {p3, p4}, {p4, p1},
        {p5, p6}, {p6, p7}, {p7, p8}, {p8, p5}}},
    {"strange", []seg{{p1, p5},
        {p5, p4}, {p4, p8}, {p8, p7}, {p7, p3}, {p3, p2}, {p2, p5}}},
    {"exagon", []seg{{p11, p12}, {p12, p10}, {p10, p13},
        {p13, p14}, {p14, p9}, {p9, p11}}},
}

var tpt = []xy{
    
    {5, 5}, {5, 8}, {-10, 5}, {0, 5}, {10, 5}, {8, 5}, {10, 10},
    
    {1, 2}, {2, 1},
}

func main() {
    for _, pg := range tpg {
        fmt.Printf("%s:\n", pg.name)
        for _, pt := range tpt {
            fmt.Println(pt, inside(pt, pg))
        }
    }
}



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;
    }
}