NearestSearch::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 2
c 4
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace KDTree\Search;
6
7
use KDTree\Interfaces\{KDTreeInterface, NearestSearchInterface, NodeInterface, PointInterface};
8
9
/**
10
 * Class NearestSearch
11
 *
12
 * @package KDTree\Search
13
 */
14
final class NearestSearch implements NearestSearchInterface
15
{
16
    /**
17
     * @var KDTreeInterface
18
     */
19
    private $tree;
20
21
    /**
22
     * @var int
23
     */
24
    private $visited = 0;
25
26
    /**
27
     * @var PointInterface|null
28
     */
29
    private $closestPoint;
30
31
    /**
32
     * @var float
33
     */
34
    private $bestDistance;
35
36
    /**
37
     * @param KDTreeInterface $tree
38
     */
39
    public function __construct(KDTreeInterface $tree)
40
    {
41
        $this->tree = $tree;
42
        $this->reset();
43
    }
44
45
    /**
46
     * @param PointInterface $point
47
     *
48
     * @return PointInterface|null
49
     */
50
    public function nearest(PointInterface $point): ?PointInterface
51
    {
52
        $this->reset();
53
        $this->findNearest($this->tree->getRoot(), $point);
54
55
        return $this->closestPoint;
56
    }
57
58
    /**
59
     * @return float
60
     */
61
    public function getNearestDistance(): float
62
    {
63
        return $this->bestDistance;
64
    }
65
66
    /**
67
     * @param NodeInterface|null $node
68
     * @param PointInterface     $searchingPoint
69
     * @param int                $cuttingDimension
70
     */
71
    private function findNearest(?NodeInterface $node, PointInterface $searchingPoint, int $cuttingDimension = 0): void
72
    {
73
        if (null === $node) {
74
            return;
75
        }
76
77
        ++$this->visited;
78
        $this->chooseBestDistance($node, $searchingPoint);
79
80
        if (0.0 >= $this->bestDistance) {
81
            return;
82
        }
83
84
        $dx = $node->getPoint()->getDAxis($cuttingDimension) - $searchingPoint->getDAxis($cuttingDimension);
85
        $cuttingDimension = ($cuttingDimension + 1) % $this->tree->getDimensions();
86
87
        $this->findNearest($dx > 0 ? $node->getLeft() : $node->getRight(), $searchingPoint, $cuttingDimension);
88
        if ($dx * $dx >= $this->bestDistance) {
89
            return;
90
        }
91
92
        $this->findNearest($dx > 0 ? $node->getRight() : $node->getLeft(), $searchingPoint, $cuttingDimension);
93
    }
94
95
    private function reset(): void
96
    {
97
        $this->bestDistance = (float)PHP_INT_MAX;
98
        $this->closestPoint = null;
99
        $this->visited = 0;
100
    }
101
102
    /**
103
     * @param NodeInterface  $pretenderNode
104
     * @param PointInterface $searchingPoint
105
     */
106
    private function chooseBestDistance(NodeInterface $pretenderNode, PointInterface $searchingPoint): void
107
    {
108
        $pretenderDistance = $pretenderNode->getPoint()->distance($searchingPoint);
109
110
        if (null === $this->closestPoint || $pretenderDistance < $this->bestDistance) {
111
            $this->bestDistance = $pretenderDistance;
112
            $this->closestPoint = $pretenderNode->getPoint();
113
        }
114
    }
115
}
116