Completed
Push — master ( 5bcf33...3feb07 )
by Dan
27:10
created

QuadTree::__toString()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 0
cts 13
cp 0
rs 8.7972
c 0
b 0
f 0
cc 4
eloc 14
nc 4
nop 0
crap 20
1
<?php
2
3
namespace SixtyNine\Cloud\Model;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
7
class QuadTree
8
{
9
    const MAX_LEVELS = 10;
10
11
    protected $level;
12
    protected $objects;
13
    protected $bounds;
14
    protected $isSplited = false;
15
    /** @var array QuadTree[] */
16
    protected $nodes;
17
18 20
    public function __construct(Box $bounds, $level = 0)
19
    {
20 20
        $this->level = $level;
21 20
        $this->objects = new ArrayCollection();
22 20
        $this->bounds = $bounds;
23 20
    }
24
25 8
    public function split()
26
    {
27 8
        $this->isSplited = true;
28
29 8
        $subWidth = (int)($this->bounds->getWidth() / 2);
30 8
        $subHeight = (int)($this->bounds->getHeight() / 2);
31 8
        $x = (int)$this->bounds->getX();
32 8
        $y = (int)$this->bounds->getY();
33
34 8
        $this->nodes = array();
35 8
        $this->nodes[0] = new Quadtree(new Box($x, $y, $subWidth, $subHeight), $this->level + 1);
36 8
        $this->nodes[1] = new Quadtree(new Box($x + $subWidth, $y, $subWidth, $subHeight), $this->level + 1);
37 8
        $this->nodes[2] = new Quadtree(new Box($x, $y + $subHeight, $subWidth, $subHeight), $this->level + 1);
38 8
        $this->nodes[3] = new Quadtree(new Box($x + $subWidth, $y + $subHeight, $subWidth, $subHeight), $this->level + 1);
39 8
    }
40
41 18
    public function getIndex(Box $box)
42
    {
43 18
        $vMidpoint = $this->bounds->getX() + ($this->bounds->getWidth() / 2);
44 18
        $hMidpoint = $this->bounds->getY() + ($this->bounds->getHeight() / 2);
45
46 18
        $topQuadrant = ($box->getY() <= $hMidpoint && $box->getY() + $box->getHeight() <= $hMidpoint);
47 18
        $bottomQuadrant = ($box->getY() >= $hMidpoint);
48 18
        $leftQuadrant = $box->getX() <= $vMidpoint && $box->getX() + $box->getWidth() <= $vMidpoint;
49 18
        $rightQuadrant = $box->getX() >= $vMidpoint;
50
51
        // Object can completely fit within the left quadrants
52 18
        if ($leftQuadrant) {
53 11
            if ($topQuadrant) {
54 9
                return 0;
55 9
            } else if ($bottomQuadrant) {
56 9
                return 2;
57
            }
58 14
        } else if ($rightQuadrant) {
59 9
            if ($topQuadrant) {
60 7
                return 1;
61 7
            } else if ($bottomQuadrant) {
62 7
                return 3;
63
            }
64
        }
65
66 10
        return -1;
67
    }
68
69 7
    public function insert(Box $box)
70
    {
71 7
        $index = $this->getIndex($box);
72
73 7
        if ($index !== -1 && $this->level < self::MAX_LEVELS) {
74 7
            if (!$this->isSplited) {
75 7
                $this->split();
76
            }
77 7
            $this->nodes[$index]->insert($box);
78 7
            return;
79
        }
80
81 7
        $this->objects->add($box);
82 7
    }
83
84 3
    public function count()
85
    {
86 3
        $count = $this->objects->count();
87
88 3
        if ($this->isSplited) {
89 2
            foreach ($this->nodes as $node) {
90 2
                $count += $node->count();
91
            }
92
        }
93
94 3
        return $count;
95
    }
96
97 1
    public function retrieve(Box $box)
98
    {
99 1
        $return = array();
100
101 1
        if ($this->isSplited) {
102
103 1
            $index = $this->getIndex($box);
104 1
            $nodes = (-1 === $index) ? $this->nodes : array($this->nodes[$index]);
105 1
            foreach ($nodes as $node) {
106 1
                $return = array_merge($return, $node->retrieve($box));
107
            }
108
        }
109
110 1
        $return = array_merge($return, $this->objects->toArray());
111
112 1
        return $return;
113
    }
114
115 5
    public function collides(Box $box)
116
    {
117 5
        if ($this->isSplited) {
118
119 5
            $index = $this->getIndex($box);
120 5
            $nodes = (-1 === $index) ? $this->nodes : array($this->nodes[$index]);
121 5
            foreach ($nodes as $node) {
122 5
                if ($node->collides($box)) {
123 5
                    return true;
124
                }
125
            }
126
        }
127
128 5
        foreach ($this->objects as $object) {
129 5
            if ($box->intersects($object)) {
130 5
                return true;
131
            }
132
        }
133
134 5
        return false;
135
    }
136
137
    public function __toString()
138
    {
139
        $padding = str_repeat('  ', $this->level);
140
        $res = sprintf(
141
            '%sQuadTree, level: %s, bounds: %s, objects: %s',
142
            $padding,
143
            $this->level,
144
            $this->bounds,
145
            $this->objects->count()
146
        );
147
148
        foreach ($this->objects as $box) {
149
            $res .= PHP_EOL . $padding . '  - ' . (string)$box;
150
        }
151
152
        if (null !== $this->nodes) {
153
            foreach ($this->nodes as $node) {
154
                $res .= PHP_EOL . (string)$node;
155
            }
156
        }
157
158
        return $res . PHP_EOL;
159
    }
160
}
161