github.com/richardwilkes/toolbox@v1.121.0/collection/quadtree/node.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  type node[T xmath.Numeric, N Node[T]] struct {
    18  	rect      geom.Rect[T]
    19  	children  [4]*node[T, N]
    20  	contents  []N
    21  	threshold int
    22  }
    23  
    24  func (n *node[T, N]) Bounds() geom.Rect[T] {
    25  	return n.rect
    26  }
    27  
    28  func (n *node[T, N]) all(result []N) []N {
    29  	result = append(result, n.contents...)
    30  	if !n.isLeaf() {
    31  		for _, child := range n.children {
    32  			result = child.all(result)
    33  		}
    34  	}
    35  	return result
    36  }
    37  
    38  func (n *node[T, N]) isLeaf() bool {
    39  	return n.children[0] == nil
    40  }
    41  
    42  func (n *node[T, N]) insert(obj N) {
    43  	n.splitIfNeeded()
    44  	if !n.isLeaf() {
    45  		rect := obj.Bounds()
    46  		for _, child := range n.children {
    47  			if child.rect.Contains(rect) {
    48  				child.insert(obj)
    49  				return
    50  			}
    51  		}
    52  	}
    53  	n.contents = append(n.contents, obj)
    54  }
    55  
    56  func (n *node[T, N]) remove(obj N) bool {
    57  	for i, one := range n.contents {
    58  		if one != obj {
    59  			continue
    60  		}
    61  		n.contents[i] = n.contents[len(n.contents)-1]
    62  		var zero N
    63  		n.contents[len(n.contents)-1] = zero
    64  		n.contents = n.contents[:len(n.contents)-1]
    65  		return true
    66  	}
    67  	if !n.isLeaf() && n.rect.Contains(obj.Bounds()) {
    68  		for _, child := range n.children {
    69  			if child.remove(obj) {
    70  				return true
    71  			}
    72  		}
    73  	}
    74  	return false
    75  }
    76  
    77  func (n *node[T, N]) splitIfNeeded() {
    78  	if n.isLeaf() {
    79  		if len(n.contents) >= n.threshold {
    80  			hw := n.rect.Width / 2
    81  			hh := n.rect.Height / 2
    82  			n.children[0] = &node[T, N]{
    83  				rect: geom.Rect[T]{
    84  					Point: n.rect.Point,
    85  					Size:  geom.NewSize[T](hw, hw),
    86  				},
    87  				threshold: n.threshold,
    88  			}
    89  			n.children[1] = &node[T, N]{
    90  				rect:      geom.NewRect[T](n.rect.X+hw, n.rect.Y, n.rect.Width-hw, hh),
    91  				threshold: n.threshold,
    92  			}
    93  			n.children[2] = &node[T, N]{
    94  				rect:      geom.NewRect[T](n.rect.X, n.rect.Y+hh, hw, n.rect.Height-hh),
    95  				threshold: n.threshold,
    96  			}
    97  			n.children[3] = &node[T, N]{
    98  				rect:      geom.NewRect[T](n.rect.X+hw, n.rect.Y+hh, n.rect.Width-hw, n.rect.Height-hh),
    99  				threshold: n.threshold,
   100  			}
   101  			contents := n.contents
   102  			n.contents = nil
   103  			for _, one := range contents {
   104  				n.insert(one)
   105  			}
   106  		}
   107  	}
   108  }
   109  
   110  func (n *node[T, N]) containsPoint(pt geom.Point[T]) bool {
   111  	if pt.In(n.rect) {
   112  		for _, one := range n.contents {
   113  			if pt.In(one.Bounds()) {
   114  				return true
   115  			}
   116  		}
   117  		if !n.isLeaf() {
   118  			for _, one := range n.children {
   119  				if one.containsPoint(pt) {
   120  					return true
   121  				}
   122  			}
   123  		}
   124  	}
   125  	return false
   126  }
   127  
   128  func (n *node[T, N]) findContainsPoint(pt geom.Point[T], result []N) []N {
   129  	if pt.In(n.rect) {
   130  		for _, one := range n.contents {
   131  			if pt.In(one.Bounds()) {
   132  				result = append(result, one)
   133  			}
   134  		}
   135  		if !n.isLeaf() {
   136  			for _, one := range n.children {
   137  				result = one.findContainsPoint(pt, result)
   138  			}
   139  		}
   140  	}
   141  	return result
   142  }
   143  
   144  func (n *node[T, N]) matchedContainsPoint(matcher Matcher[T, N], pt geom.Point[T]) bool {
   145  	if pt.In(n.rect) {
   146  		for _, one := range n.contents {
   147  			if pt.In(one.Bounds()) && matcher.Matches(one) {
   148  				return true
   149  			}
   150  		}
   151  		if !n.isLeaf() {
   152  			for _, one := range n.children {
   153  				if one.matchedContainsPoint(matcher, pt) {
   154  					return true
   155  				}
   156  			}
   157  		}
   158  	}
   159  	return false
   160  }
   161  
   162  func (n *node[T, N]) findMatchedContainsPoint(matcher Matcher[T, N], pt geom.Point[T], result []N) []N {
   163  	if pt.In(n.rect) {
   164  		for _, one := range n.contents {
   165  			if pt.In(one.Bounds()) && matcher.Matches(one) {
   166  				result = append(result, one)
   167  			}
   168  		}
   169  		if !n.isLeaf() {
   170  			for _, one := range n.children {
   171  				result = one.findMatchedContainsPoint(matcher, pt, result)
   172  			}
   173  		}
   174  	}
   175  	return result
   176  }
   177  
   178  func (n *node[T, N]) intersects(rect geom.Rect[T]) bool {
   179  	if n.rect.Intersects(rect) {
   180  		for _, one := range n.contents {
   181  			if one.Bounds().Intersects(rect) {
   182  				return true
   183  			}
   184  		}
   185  		if !n.isLeaf() {
   186  			for _, one := range n.children {
   187  				if one.intersects(rect) {
   188  					return true
   189  				}
   190  			}
   191  		}
   192  	}
   193  	return false
   194  }
   195  
   196  func (n *node[T, N]) findIntersects(rect geom.Rect[T], result []N) []N {
   197  	if n.rect.Intersects(rect) {
   198  		for _, one := range n.contents {
   199  			if one.Bounds().Intersects(rect) {
   200  				result = append(result, one)
   201  			}
   202  		}
   203  		if !n.isLeaf() {
   204  			for _, one := range n.children {
   205  				result = one.findIntersects(rect, result)
   206  			}
   207  		}
   208  	}
   209  	return result
   210  }
   211  
   212  func (n *node[T, N]) matchedIntersects(matcher Matcher[T, N], rect geom.Rect[T]) bool {
   213  	if n.rect.Intersects(rect) {
   214  		for _, one := range n.contents {
   215  			if one.Bounds().Intersects(rect) && matcher.Matches(one) {
   216  				return true
   217  			}
   218  		}
   219  		if !n.isLeaf() {
   220  			for _, one := range n.children {
   221  				if one.matchedIntersects(matcher, rect) {
   222  					return true
   223  				}
   224  			}
   225  		}
   226  	}
   227  	return false
   228  }
   229  
   230  func (n *node[T, N]) findMatchedIntersects(matcher Matcher[T, N], rect geom.Rect[T], result []N) []N {
   231  	if n.rect.Intersects(rect) {
   232  		for _, one := range n.contents {
   233  			if one.Bounds().Intersects(rect) && matcher.Matches(one) {
   234  				result = append(result, one)
   235  			}
   236  		}
   237  		if !n.isLeaf() {
   238  			for _, one := range n.children {
   239  				result = one.findMatchedIntersects(matcher, rect, result)
   240  			}
   241  		}
   242  	}
   243  	return result
   244  }
   245  
   246  func (n *node[T, N]) containsRect(rect geom.Rect[T]) bool {
   247  	if n.rect.Intersects(rect) {
   248  		for _, one := range n.contents {
   249  			if one.Bounds().Contains(rect) {
   250  				return true
   251  			}
   252  		}
   253  		if !n.isLeaf() {
   254  			for _, one := range n.children {
   255  				if one.containsRect(rect) {
   256  					return true
   257  				}
   258  			}
   259  		}
   260  	}
   261  	return false
   262  }
   263  
   264  func (n *node[T, N]) findContainsRect(rect geom.Rect[T], result []N) []N {
   265  	if n.rect.Intersects(rect) {
   266  		for _, one := range n.contents {
   267  			if one.Bounds().Contains(rect) {
   268  				result = append(result, one)
   269  			}
   270  		}
   271  		if !n.isLeaf() {
   272  			for _, one := range n.children {
   273  				result = one.findContainsRect(rect, result)
   274  			}
   275  		}
   276  	}
   277  	return result
   278  }
   279  
   280  func (n *node[T, N]) matchedContainsRect(matcher Matcher[T, N], rect geom.Rect[T]) bool {
   281  	if n.rect.Intersects(rect) {
   282  		for _, one := range n.contents {
   283  			if one.Bounds().Contains(rect) && matcher.Matches(one) {
   284  				return true
   285  			}
   286  		}
   287  		if !n.isLeaf() {
   288  			for _, one := range n.children {
   289  				if one.matchedContainsRect(matcher, rect) {
   290  					return true
   291  				}
   292  			}
   293  		}
   294  	}
   295  	return false
   296  }
   297  
   298  func (n *node[T, N]) findMatchedContainsRect(matcher Matcher[T, N], rect geom.Rect[T], result []N) []N {
   299  	if n.rect.Intersects(rect) {
   300  		for _, one := range n.contents {
   301  			if one.Bounds().Contains(rect) && matcher.Matches(one) {
   302  				result = append(result, one)
   303  			}
   304  		}
   305  		if !n.isLeaf() {
   306  			for _, one := range n.children {
   307  				result = one.findMatchedContainsRect(matcher, rect, result)
   308  			}
   309  		}
   310  	}
   311  	return result
   312  }
   313  
   314  func (n *node[T, N]) containedByRect(rect geom.Rect[T]) bool {
   315  	if n.rect.Intersects(rect) {
   316  		for _, one := range n.contents {
   317  			if rect.Contains(one.Bounds()) {
   318  				return true
   319  			}
   320  		}
   321  		if !n.isLeaf() {
   322  			for _, one := range n.children {
   323  				if one.containedByRect(rect) {
   324  					return true
   325  				}
   326  			}
   327  		}
   328  	}
   329  	return false
   330  }
   331  
   332  func (n *node[T, N]) findContainedByRect(rect geom.Rect[T], result []N) []N {
   333  	if n.rect.Intersects(rect) {
   334  		for _, one := range n.contents {
   335  			if rect.Contains(one.Bounds()) {
   336  				result = append(result, one)
   337  			}
   338  		}
   339  		if !n.isLeaf() {
   340  			for _, one := range n.children {
   341  				result = one.findContainedByRect(rect, result)
   342  			}
   343  		}
   344  	}
   345  	return result
   346  }
   347  
   348  func (n *node[T, N]) matchedContainedByRect(matcher Matcher[T, N], rect geom.Rect[T]) bool {
   349  	if n.rect.Intersects(rect) {
   350  		for _, one := range n.contents {
   351  			if rect.Contains(one.Bounds()) && matcher.Matches(one) {
   352  				return true
   353  			}
   354  		}
   355  		if !n.isLeaf() {
   356  			for _, one := range n.children {
   357  				if one.matchedContainedByRect(matcher, rect) {
   358  					return true
   359  				}
   360  			}
   361  		}
   362  	}
   363  	return false
   364  }
   365  
   366  func (n *node[T, N]) findMatchedContainedByRect(matcher Matcher[T, N], rect geom.Rect[T], result []N) []N {
   367  	if n.rect.Intersects(rect) {
   368  		for _, one := range n.contents {
   369  			if rect.Contains(one.Bounds()) && matcher.Matches(one) {
   370  				result = append(result, one)
   371  			}
   372  		}
   373  		if !n.isLeaf() {
   374  			for _, one := range n.children {
   375  				result = one.findMatchedContainedByRect(matcher, rect, result)
   376  			}
   377  		}
   378  	}
   379  	return result
   380  }