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  }