1+ import { Rectangle } from "./Rectangle" ;
2+ import { ShapeEntity } from "./SpatialHash" ;
3+
4+ export class QuadTree {
5+ private readonly bounds : Rectangle ;
6+ private readonly capacity : number ;
7+ private readonly maxDepth : number ;
8+ private readonly depth : number ;
9+ private readonly entities : ShapeEntity [ ] ;
10+ private divided : boolean ;
11+
12+ private northWest ?: QuadTree ;
13+ private northEast ?: QuadTree ;
14+ private southWest ?: QuadTree ;
15+ private southEast ?: QuadTree ;
16+
17+ constructor ( bounds : Rectangle , capacity = 4 , maxDepth = 8 , depth = 0 ) {
18+ this . bounds = bounds ;
19+ this . capacity = capacity ;
20+ this . maxDepth = maxDepth ;
21+ this . depth = depth ;
22+ this . entities = [ ] ;
23+ this . divided = false ;
24+ }
25+
26+ public insert ( entity : ShapeEntity ) : boolean {
27+ // If entity doesn't intersect this quadrant, reject
28+ if ( ! this . bounds . intersects ( entity . shape . getBounds ( ) ) ) {
29+ return false ;
30+ }
31+
32+ // If capacity not reached and not divided, add here
33+ if ( this . entities . length < this . capacity && ! this . divided ) {
34+ this . entities . push ( entity ) ;
35+ return true ;
36+ }
37+
38+ // Subdivide if needed
39+ if ( ! this . divided ) {
40+ this . subdivide ( ) ;
41+ }
42+
43+ // Try to insert into children
44+ return (
45+ this . northWest ! . insert ( entity ) ||
46+ this . northEast ! . insert ( entity ) ||
47+ this . southWest ! . insert ( entity ) ||
48+ this . southEast ! . insert ( entity )
49+ ) ;
50+ }
51+
52+ private subdivide ( ) : void {
53+ if ( this . depth >= this . maxDepth ) {
54+ return ; // Max depth reached
55+ }
56+
57+ const x = this . bounds . getPosition ( ) . x ;
58+ const y = this . bounds . getPosition ( ) . y ;
59+ const w = this . bounds . getWidth ( ) / 2 ;
60+ const h = this . bounds . getHeight ( ) / 2 ;
61+
62+ this . northWest = new QuadTree (
63+ new Rectangle ( x , y , w , h ) ,
64+ this . capacity ,
65+ this . maxDepth ,
66+ this . depth + 1
67+ ) ;
68+ this . northEast = new QuadTree (
69+ new Rectangle ( x + w , y , w , h ) ,
70+ this . capacity ,
71+ this . maxDepth ,
72+ this . depth + 1
73+ ) ;
74+ this . southWest = new QuadTree (
75+ new Rectangle ( x , y + h , w , h ) ,
76+ this . capacity ,
77+ this . maxDepth ,
78+ this . depth + 1
79+ ) ;
80+ this . southEast = new QuadTree (
81+ new Rectangle ( x + w , y + h , w , h ) ,
82+ this . capacity ,
83+ this . maxDepth ,
84+ this . depth + 1
85+ ) ;
86+
87+ // Re-insert existing entities into children
88+ const entitiesToRedistribute = [ ...this . entities ] ;
89+ this . entities . length = 0 ;
90+
91+ for ( const entity of entitiesToRedistribute ) {
92+ this . northWest . insert ( entity ) ||
93+ this . northEast . insert ( entity ) ||
94+ this . southWest . insert ( entity ) ||
95+ this . southEast . insert ( entity ) ;
96+ }
97+
98+ this . divided = true ;
99+ }
100+
101+ public query ( range : Rectangle , found : ShapeEntity [ ] = [ ] ) : ShapeEntity [ ] {
102+ // No intersection, return
103+ if ( ! this . bounds . intersects ( range ) ) {
104+ return found ;
105+ }
106+
107+ // Check entities in this node
108+ for ( const entity of this . entities ) {
109+ if ( range . intersects ( entity . shape . getBounds ( ) ) ) {
110+ found . push ( entity ) ;
111+ }
112+ }
113+
114+ // Recursively check children
115+ if ( this . divided ) {
116+ this . northWest ! . query ( range , found ) ;
117+ this . northEast ! . query ( range , found ) ;
118+ this . southWest ! . query ( range , found ) ;
119+ this . southEast ! . query ( range , found ) ;
120+ }
121+
122+ return found ;
123+ }
124+
125+ public clear ( ) : void {
126+ this . entities . length = 0 ;
127+ this . divided = false ;
128+ this . northWest = undefined ;
129+ this . northEast = undefined ;
130+ this . southWest = undefined ;
131+ this . southEast = undefined ;
132+ }
133+
134+ public getAllEntities ( ) : ShapeEntity [ ] {
135+ let all = [ ...this . entities ] ;
136+
137+ if ( this . divided ) {
138+ all = all . concat (
139+ this . northWest ! . getAllEntities ( ) ,
140+ this . northEast ! . getAllEntities ( ) ,
141+ this . southWest ! . getAllEntities ( ) ,
142+ this . southEast ! . getAllEntities ( )
143+ ) ;
144+ }
145+
146+ return all ;
147+ }
148+ }
0 commit comments