github.com/richardwilkes/toolbox@v1.121.0/collection/quadtree/quadtree.go (about) 1 // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, version 2.0. If a copy of the MPL was not distributed with 5 // this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 // 7 // This Source Code Form is "Incompatible With Secondary Licenses", as 8 // defined by the Mozilla Public License, version 2.0. 9 10 package quadtree 11 12 import ( 13 "github.com/richardwilkes/toolbox/xmath" 14 "github.com/richardwilkes/toolbox/xmath/geom" 15 ) 16 17 const ( 18 // DefaultQuadTreeThreshold is the default threshold that will be used if none is specified. 19 DefaultQuadTreeThreshold = 64 20 // MinQuadTreeThreshold is the minimum allowed threshold. 21 MinQuadTreeThreshold = 4 22 ) 23 24 // Node defines the methods an object that can be stored within the QuadTree must implement. 25 type Node[T xmath.Numeric] interface { 26 comparable 27 // Bounds returns the node's bounding rectangle. 28 Bounds() geom.Rect[T] 29 } 30 31 // Matcher is used to match nodes. 32 type Matcher[T xmath.Numeric, N Node[T]] interface { 33 // Matches returns true if the node matches. 34 Matches(n N) bool 35 } 36 37 // QuadTree stores two-dimensional nodes for fast lookup. 38 type QuadTree[T xmath.Numeric, N Node[T]] struct { 39 root *node[T, N] 40 outside []N 41 Threshold int 42 count int 43 } 44 45 // Size returns the number of nodes contained within the QuadTree. 46 func (q *QuadTree[T, N]) Size() int { 47 return q.count 48 } 49 50 func (q *QuadTree[T, N]) threshold() int { 51 if q.Threshold < MinQuadTreeThreshold { 52 return DefaultQuadTreeThreshold 53 } 54 return q.Threshold 55 } 56 57 // Insert a node. NOTE: Once a node is inserted, the value it returns from a call to Bounds() MUST REMAIN THE SAME until 58 // the node is removed. 59 func (q *QuadTree[T, N]) Insert(n N) { 60 rect := n.Bounds() 61 if rect.Empty() { 62 return 63 } 64 q.count++ 65 if q.root != nil && q.root.rect.Contains(rect) { 66 q.root.insert(n) 67 } else { 68 q.outside = append(q.outside, n) 69 if len(q.outside) > q.threshold() { 70 q.Reorganize() 71 } 72 } 73 } 74 75 // Remove a node. 76 func (q *QuadTree[T, N]) Remove(n N) { 77 for i, one := range q.outside { 78 if one != n { 79 continue 80 } 81 q.outside[i] = q.outside[len(q.outside)-1] 82 var zero N 83 q.outside[len(q.outside)-1] = zero 84 q.outside = q.outside[:len(q.outside)-1] 85 q.count-- 86 return 87 } 88 if q.root != nil { 89 if q.root.remove(n) { 90 q.count-- 91 } 92 } 93 } 94 95 // All returns all nodes. 96 func (q *QuadTree[T, N]) All() []N { 97 all := make([]N, 0, q.count) 98 all = append(all, q.outside...) 99 if q.root != nil { 100 all = q.root.all(all) 101 } 102 return all 103 } 104 105 // Reorganize the QuadTree to optimally fit its contents. 106 func (q *QuadTree[T, N]) Reorganize() { 107 all := q.All() 108 var rect geom.Rect[T] 109 for _, one := range all { 110 rect = rect.Union(one.Bounds()) 111 } 112 q.root = nil 113 q.outside = nil 114 if len(all) > 0 { 115 q.root = &node[T, N]{ 116 rect: rect, 117 threshold: q.threshold(), 118 } 119 for _, one := range all { 120 q.root.insert(one) 121 } 122 } 123 } 124 125 // Clear removes all nodes. 126 func (q *QuadTree[T, N]) Clear() { 127 q.count = 0 128 q.root = nil 129 q.outside = nil 130 } 131 132 // ContainsPoint returns true if at least one node contains the point. 133 func (q *QuadTree[T, N]) ContainsPoint(pt geom.Point[T]) bool { 134 if q.root != nil { 135 if q.root.containsPoint(pt) { 136 return true 137 } 138 } 139 for _, one := range q.outside { 140 if pt.In(one.Bounds()) { 141 return true 142 } 143 } 144 return false 145 } 146 147 // FindContainsPoint returns the nodes that contain the point. 148 func (q *QuadTree[T, N]) FindContainsPoint(pt geom.Point[T]) []N { 149 var result []N 150 if q.root != nil { 151 result = q.root.findContainsPoint(pt, result) 152 } 153 for _, one := range q.outside { 154 if pt.In(one.Bounds()) { 155 result = append(result, one) 156 } 157 } 158 return result 159 } 160 161 // MatchedContainsPoint returns true if at least one node that the matcher returns true for contains the point. 162 func (q *QuadTree[T, N]) MatchedContainsPoint(matcher Matcher[T, N], pt geom.Point[T]) bool { 163 if q.root != nil { 164 if q.root.matchedContainsPoint(matcher, pt) { 165 return true 166 } 167 } 168 for _, one := range q.outside { 169 if pt.In(one.Bounds()) && matcher.Matches(one) { 170 return true 171 } 172 } 173 return false 174 } 175 176 // FindMatchedContainsPoint returns the nodes that the matcher returns true for which contain the point. 177 func (q *QuadTree[T, N]) FindMatchedContainsPoint(matcher Matcher[T, N], pt geom.Point[T]) []N { 178 var result []N 179 if q.root != nil { 180 result = q.root.findMatchedContainsPoint(matcher, pt, result) 181 } 182 for _, one := range q.outside { 183 if pt.In(one.Bounds()) && matcher.Matches(one) { 184 result = append(result, one) 185 } 186 } 187 return result 188 } 189 190 // Intersects returns true if at least one node intersects the rect. 191 func (q *QuadTree[T, N]) Intersects(rect geom.Rect[T]) bool { 192 if q.root != nil { 193 if q.root.intersects(rect) { 194 return true 195 } 196 } 197 for _, one := range q.outside { 198 if one.Bounds().Intersects(rect) { 199 return true 200 } 201 } 202 return false 203 } 204 205 // FindIntersects returns the nodes that intersect the rect. 206 func (q *QuadTree[T, N]) FindIntersects(rect geom.Rect[T]) []N { 207 var result []N 208 if q.root != nil { 209 result = q.root.findIntersects(rect, result) 210 } 211 for _, one := range q.outside { 212 if one.Bounds().Intersects(rect) { 213 result = append(result, one) 214 } 215 } 216 return result 217 } 218 219 // MatchedIntersects returns true if at least one node that the matcher returns true for intersects the rect. 220 func (q *QuadTree[T, N]) MatchedIntersects(matcher Matcher[T, N], rect geom.Rect[T]) bool { 221 if q.root != nil { 222 if q.root.matchedIntersects(matcher, rect) { 223 return true 224 } 225 } 226 for _, one := range q.outside { 227 if one.Bounds().Intersects(rect) && matcher.Matches(one) { 228 return true 229 } 230 } 231 return false 232 } 233 234 // FindMatchedIntersects returns the nodes that the matcher returns true for which intersect the rect. 235 func (q *QuadTree[T, N]) FindMatchedIntersects(matcher Matcher[T, N], rect geom.Rect[T]) []N { 236 var result []N 237 if q.root != nil { 238 result = q.root.findMatchedIntersects(matcher, rect, result) 239 } 240 for _, one := range q.outside { 241 if one.Bounds().Intersects(rect) && matcher.Matches(one) { 242 result = append(result, one) 243 } 244 } 245 return result 246 } 247 248 // ContainsRect returns true if at least one node contains the rect. 249 func (q *QuadTree[T, N]) ContainsRect(rect geom.Rect[T]) bool { 250 if q.root != nil { 251 if q.root.containsRect(rect) { 252 return true 253 } 254 } 255 for _, one := range q.outside { 256 if one.Bounds().Contains(rect) { 257 return true 258 } 259 } 260 return false 261 } 262 263 // FindContainsRect returns the nodes that contain the rect. 264 func (q *QuadTree[T, N]) FindContainsRect(rect geom.Rect[T]) []N { 265 var result []N 266 if q.root != nil { 267 result = q.root.findContainsRect(rect, result) 268 } 269 for _, one := range q.outside { 270 if one.Bounds().Contains(rect) { 271 result = append(result, one) 272 } 273 } 274 return result 275 } 276 277 // MatchedContainsRect returns true if at least one node that the matcher returns true for contains the rect. 278 func (q *QuadTree[T, N]) MatchedContainsRect(matcher Matcher[T, N], rect geom.Rect[T]) bool { 279 if q.root != nil { 280 if q.root.matchedContainsRect(matcher, rect) { 281 return true 282 } 283 } 284 for _, one := range q.outside { 285 if one.Bounds().Contains(rect) && matcher.Matches(one) { 286 return true 287 } 288 } 289 return false 290 } 291 292 // FindMatchedContainsRect returns the nodes that the matcher returns true for which contains the rect. 293 func (q *QuadTree[T, N]) FindMatchedContainsRect(matcher Matcher[T, N], rect geom.Rect[T]) []N { 294 var result []N 295 if q.root != nil { 296 result = q.root.findMatchedContainsRect(matcher, rect, result) 297 } 298 for _, one := range q.outside { 299 if one.Bounds().Contains(rect) && matcher.Matches(one) { 300 result = append(result, one) 301 } 302 } 303 return result 304 } 305 306 // ContainedByRect returns true if at least one node is contained by the rect. 307 func (q *QuadTree[T, N]) ContainedByRect(rect geom.Rect[T]) bool { 308 if q.root != nil { 309 if q.root.containedByRect(rect) { 310 return true 311 } 312 } 313 for _, one := range q.outside { 314 if rect.Contains(one.Bounds()) { 315 return true 316 } 317 } 318 return false 319 } 320 321 // FindContainedByRect returns the nodes that are contained by the rect. 322 func (q *QuadTree[T, N]) FindContainedByRect(rect geom.Rect[T]) []N { 323 var result []N 324 if q.root != nil { 325 result = q.root.findContainedByRect(rect, result) 326 } 327 for _, one := range q.outside { 328 if rect.Contains(one.Bounds()) { 329 result = append(result, one) 330 } 331 } 332 return result 333 } 334 335 // MatchedContainedByRect returns true if at least one node that the matcher returns true for is contained by the rect. 336 func (q *QuadTree[T, N]) MatchedContainedByRect(matcher Matcher[T, N], rect geom.Rect[T]) bool { 337 if q.root != nil { 338 if q.root.matchedContainedByRect(matcher, rect) { 339 return true 340 } 341 } 342 for _, one := range q.outside { 343 if rect.Contains(one.Bounds()) && matcher.Matches(one) { 344 return true 345 } 346 } 347 return false 348 } 349 350 // FindMatchedContainedByRect returns the nodes that the matcher returns true for which are contained by the rect. 351 func (q *QuadTree[T, N]) FindMatchedContainedByRect(matcher Matcher[T, N], rect geom.Rect[T]) []N { 352 var result []N 353 if q.root != nil { 354 result = q.root.findMatchedContainedByRect(matcher, rect, result) 355 } 356 for _, one := range q.outside { 357 if rect.Contains(one.Bounds()) && matcher.Matches(one) { 358 result = append(result, one) 359 } 360 } 361 return result 362 }