quad_tree.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. package aoi
  2. import (
  3. "github.com/fogleman/gg"
  4. "log"
  5. "math/rand"
  6. "rocommon/util"
  7. "strconv"
  8. "time"
  9. )
  10. //四叉树碰撞检测
  11. var dc = gg.NewContext(800, 800)
  12. var MaxObjNum int32 = 2
  13. type QuadBounds struct {
  14. X float32
  15. Y float32
  16. Width float32
  17. Height float32
  18. }
  19. type QuadTreeNode struct {
  20. MaxObjNum int32 //split之前最大能存储的节点数量
  21. MaxLevels int32 //最大深度
  22. Level int32 //当前深度
  23. Rect *QuadBounds //当前节点的bound
  24. ObjList []*AoiObject //当前节点存储的对象数量
  25. Nodes []*QuadTreeNode //子节点
  26. }
  27. func newQuadTreeNode(level int32, maxLevel int32, rect *QuadBounds) *QuadTreeNode {
  28. node := &QuadTreeNode{
  29. MaxLevels: maxLevel,
  30. Level: level,
  31. Rect: rect,
  32. MaxObjNum: MaxObjNum,
  33. }
  34. return node
  35. }
  36. func NewQuadTree(maxLevel int32, rect *QuadBounds) *QuadTreeNode {
  37. root := &QuadTreeNode{
  38. MaxLevels: maxLevel,
  39. Rect: rect,
  40. MaxObjNum: MaxObjNum,
  41. }
  42. return root
  43. }
  44. func (this *QuadTreeNode) Clear() {
  45. this.ObjList = []*AoiObject{}
  46. for idx := 0; idx < len(this.Nodes); idx++ {
  47. this.Nodes[idx].Clear()
  48. }
  49. this.Nodes = []*QuadTreeNode{}
  50. this.Level = 0
  51. }
  52. //split当前节点成4个子节点
  53. func (this *QuadTreeNode) Split() {
  54. subWidth := this.Rect.Width * 0.5
  55. subHeight := this.Rect.Height * 0.5
  56. x := this.Rect.X
  57. y := this.Rect.Y
  58. //-------
  59. //|2 | 3| top
  60. //-------
  61. //|1 | 0| bottom
  62. //-------
  63. //^
  64. //|(y)
  65. //|
  66. //|
  67. //|——————————>(x)
  68. this.Nodes = append(this.Nodes, newQuadTreeNode(this.Level+1, this.MaxLevels,
  69. &QuadBounds{X: x + subWidth, Y: y, Width: subWidth, Height: subHeight}))
  70. this.Nodes = append(this.Nodes, newQuadTreeNode(this.Level+1, this.MaxLevels,
  71. &QuadBounds{X: x, Y: y, Width: subWidth, Height: subHeight}))
  72. this.Nodes = append(this.Nodes, newQuadTreeNode(this.Level+1, this.MaxLevels,
  73. &QuadBounds{X: x, Y: y + subHeight, Width: subWidth, Height: subHeight}))
  74. this.Nodes = append(this.Nodes, newQuadTreeNode(this.Level+1, this.MaxLevels,
  75. &QuadBounds{X: x + subWidth, Y: y + subWidth, Width: subWidth, Height: subHeight}))
  76. //dc.SetLineWidth(float64(0.2))
  77. //dc.DrawRectangle(float64(x+subWidth), float64(y), float64(subWidth), float64(subHeight))
  78. //dc.DrawRectangle(float64(x), float64(y), float64(subWidth), float64(subHeight))
  79. //dc.DrawRectangle(float64(x), float64(y+subHeight), float64(subWidth), float64(subHeight))
  80. //dc.DrawRectangle(float64(x+subWidth), float64(y+subHeight), float64(subWidth), float64(subHeight))
  81. //dc.Stroke()
  82. }
  83. //获取包含rect的节点序号0-3
  84. func (this *QuadTreeNode) GetIndexes(rect *QuadBounds) []int32 {
  85. var indexList []int32
  86. verticalMidPoint := this.Rect.X + this.Rect.Width*0.5
  87. horizonMidPoint := this.Rect.Y + this.Rect.Height*0.5
  88. //判断上下边界
  89. topQuadrant := rect.Y >= horizonMidPoint
  90. bottomQuadrant := rect.Y-rect.Height <= horizonMidPoint
  91. topAndBottomQuadrant := rect.Y+rect.Height >= horizonMidPoint && rect.Y <= horizonMidPoint
  92. if topAndBottomQuadrant {
  93. bottomQuadrant = false
  94. topQuadrant = false
  95. }
  96. //判断是否同时在左右两边
  97. if rect.X+rect.Width >= verticalMidPoint && rect.X <= verticalMidPoint {
  98. if topQuadrant {
  99. indexList = append(indexList, 2, 3)
  100. } else if bottomQuadrant {
  101. indexList = append(indexList, 0, 1)
  102. } else if topAndBottomQuadrant {
  103. indexList = append(indexList, 0, 1, 2, 3)
  104. }
  105. } else if rect.X >= verticalMidPoint {
  106. //判断只在右边
  107. if topQuadrant {
  108. indexList = append(indexList, 3)
  109. } else if bottomQuadrant {
  110. indexList = append(indexList, 0)
  111. } else if topAndBottomQuadrant {
  112. indexList = append(indexList, 0, 3)
  113. }
  114. } else if rect.X+rect.Width <= verticalMidPoint {
  115. //判断只在左边
  116. if topQuadrant {
  117. indexList = append(indexList, 2)
  118. } else if bottomQuadrant {
  119. indexList = append(indexList, 1)
  120. } else if topAndBottomQuadrant {
  121. indexList = append(indexList, 1, 2)
  122. }
  123. }
  124. return indexList
  125. }
  126. func (this *QuadTreeNode) Insert(obj *AoiObject) {
  127. if len(this.Nodes) > 0 {
  128. indexList := this.GetIndexes(obj.rect)
  129. if len(indexList) > 0 {
  130. for idx := 0; idx < len(indexList); idx++ {
  131. this.Nodes[indexList[idx]].Insert(obj)
  132. }
  133. return
  134. }
  135. }
  136. this.ObjList = append(this.ObjList, obj)
  137. if len(this.ObjList) > int(this.MaxObjNum) && this.Level < this.MaxLevels {
  138. //split
  139. if len(this.Nodes) <= 0 {
  140. this.Split()
  141. }
  142. idx := 0
  143. for idx < len(this.ObjList) {
  144. indexList := this.GetIndexes(this.ObjList[idx].rect)
  145. if len(indexList) > 0 {
  146. for k := 0; k < len(indexList); k++ {
  147. this.Nodes[indexList[k]].Insert(this.ObjList[idx])
  148. }
  149. this.ObjList = append(this.ObjList[:idx], this.ObjList[idx+1:]...)
  150. } else {
  151. idx++
  152. }
  153. }
  154. }
  155. }
  156. func (this *QuadTreeNode) Remove(obj *AoiObject) {
  157. indexList := this.GetIndexes(obj.rect)
  158. if len(this.Nodes) > 0 {
  159. for idx := 0; idx < len(indexList); idx++ {
  160. this.Nodes[indexList[idx]].Remove(obj)
  161. }
  162. //for idx := 0; idx < len(this.Nodes); idx++ {
  163. // if len(this.Nodes[idx].ObjList) > 0 {
  164. // return
  165. // }
  166. //}
  167. //this.Nodes = nil
  168. } else {
  169. for idx := 0; idx < len(this.ObjList); idx++ {
  170. if this.ObjList[idx].Id == obj.Id {
  171. this.ObjList = append(this.ObjList[:idx], this.ObjList[idx+1:]...)
  172. break
  173. }
  174. }
  175. if len(this.ObjList) <= 0 {
  176. this.Nodes = nil
  177. }
  178. }
  179. }
  180. func (this *QuadTreeNode) Retrieve(obj *AoiObject, ObjList *[]*AoiObject) {
  181. indexList := this.GetIndexes(obj.rect)
  182. if len(this.ObjList) > 0 {
  183. *ObjList = append(*ObjList, this.ObjList...)
  184. }
  185. if len(this.Nodes) > 0 {
  186. if len(indexList) > 0 {
  187. for idx := 0; idx < len(indexList); idx++ {
  188. this.Nodes[indexList[idx]].Retrieve(obj, ObjList)
  189. }
  190. } else {
  191. for idx := 0; idx < len(this.Nodes); idx++ {
  192. this.Nodes[idx].Retrieve(obj, ObjList)
  193. }
  194. }
  195. }
  196. }
  197. func TestQuadtreePng() {
  198. rand.Seed(int64(util.GetTimeMilliseconds()))
  199. dc.SetRGB(0, 0, 0)
  200. dc.Clear()
  201. r := rand.Float64()
  202. g := rand.Float64()
  203. b := rand.Float64()
  204. ww := 0.2
  205. dc.SetRGBA(r, g, b, float64(1))
  206. dc.SetLineWidth(float64(ww))
  207. quadTest := NewQuadTree(10, &QuadBounds{0, 0, 800, 800})
  208. var allObjList []*AoiObject
  209. //nowTime := time.Now()
  210. var grid float32 = 10.0
  211. gridh := quadTest.Rect.Width / grid
  212. gridv := quadTest.Rect.Height / grid
  213. for idx := 0; idx < 100; idx++ {
  214. x := float32(rand.Int31n(int32(gridh))) * grid
  215. y := float32(rand.Int31n(int32(gridv))) * grid
  216. w := float32(rand.Intn(4)+1) * grid
  217. h := float32(rand.Intn(4)+1) * grid
  218. //w := 1
  219. //h := 1
  220. obj := &AoiObject{
  221. rect: &QuadBounds{x, y, float32(w), float32(h)},
  222. }
  223. //a := rand.Float64()*0.5 + 0.5
  224. ww := 1
  225. dc.SetRGBA(1, 1, 1, float64(100))
  226. dc.SetLineWidth(float64(ww))
  227. dc.DrawRectangle(float64(x), float64(y), float64(w), float64(h))
  228. dc.Stroke()
  229. quadTest.Insert(obj)
  230. allObjList = append(allObjList, obj)
  231. }
  232. //log.Printf("time=%v", time.Now().Sub(nowTime).String())
  233. for ii := 0; ii < 50; ii++ {
  234. dc.SetRGB(0, 0, 0)
  235. dc.Clear()
  236. var returnObjList []*AoiObject
  237. //nowTime = time.Now()
  238. //for idx := 0; idx < len(allObjList); idx++ {
  239. // quadTest.Retrieve(allObjList[idx], returnObjList)
  240. //}
  241. r = rand.Float64()
  242. g = rand.Float64()
  243. b = rand.Float64()
  244. returnObjList = returnObjList[:0]
  245. tmpObj := &AoiObject{
  246. rect: &QuadBounds{
  247. X: float32(r)*200 + float32(ii)*5,
  248. Y: float32(g)*200 + float32(ii)*5,
  249. Width: 400,
  250. Height: 400,
  251. }}
  252. nowTime := time.Now()
  253. quadTest.Retrieve(tmpObj, &returnObjList)
  254. log.Printf("time=%v", time.Now().Sub(nowTime).String())
  255. x := tmpObj.rect.X
  256. y := tmpObj.rect.Y
  257. w := tmpObj.rect.Width
  258. h := tmpObj.rect.Height
  259. dc.SetRGBA(r+22, g, b, float64(10))
  260. dc.SetLineWidth(float64(2))
  261. dc.DrawRectangle(float64(x), float64(y), float64(w), float64(h))
  262. dc.Stroke()
  263. r = rand.Float64()
  264. g = rand.Float64()
  265. b = rand.Float64()
  266. for idx := 0; idx < len(returnObjList); idx++ {
  267. x := returnObjList[idx].rect.X
  268. y := returnObjList[idx].rect.Y
  269. w := returnObjList[idx].rect.Width
  270. h := returnObjList[idx].rect.Height
  271. ww := 1
  272. dc.SetRGB(r+100, g+100, b+100)
  273. dc.SetLineWidth(float64(ww))
  274. dc.DrawRectangle(float64(x), float64(y), float64(w), float64(h))
  275. dc.Stroke()
  276. }
  277. //log.Printf("time1=%v", time.Now().Sub(nowTime).String())
  278. dc.SavePNG("rect" + strconv.Itoa(ii) + ".png")
  279. }
  280. }
  281. func TestQuadtree() {
  282. rand.Seed(int64(util.GetTimeMilliseconds()))
  283. quadTest := NewQuadTree(8, &QuadBounds{0, 0, 800, 800})
  284. var allObjList []*AoiObject
  285. nowTime := time.Now()
  286. var totalTime time.Duration
  287. var grid float32 = 10.0
  288. gridh := quadTest.Rect.Width / grid
  289. gridv := quadTest.Rect.Height / grid
  290. for idx := 0; idx < 10000; idx++ {
  291. x := float32(rand.Int31n(int32(gridh))) * grid
  292. y := float32(rand.Int31n(int32(gridv))) * grid
  293. //w := float32(rand.Intn(4)+1) * grid
  294. //h := float32(rand.Intn(4)+1) * grid
  295. w := 1
  296. h := 1
  297. obj := &AoiObject{
  298. Id: uint64(idx),
  299. rect: &QuadBounds{x, y, float32(w), float32(h)},
  300. }
  301. nowTime1 := time.Now()
  302. quadTest.Insert(obj)
  303. totalTime += time.Now().Sub(nowTime1)
  304. allObjList = append(allObjList, obj)
  305. }
  306. log.Printf("time=%v %v", time.Now().Sub(nowTime).String(), totalTime)
  307. var retrieveTime time.Duration
  308. for ii := 0; ii < len(allObjList); ii++ {
  309. var returnObjList []*AoiObject
  310. returnObjList = returnObjList[:0]
  311. nowTime := time.Now()
  312. quadTest.Retrieve(allObjList[ii], &returnObjList)
  313. retrieveTime += time.Now().Sub(nowTime)
  314. }
  315. log.Printf("retrieveTime=%v", retrieveTime.String())
  316. retrieveTime = 0
  317. nowTime = time.Now()
  318. for idx := 0; idx < len(allObjList); idx++ {
  319. quadTest.Remove(allObjList[idx])
  320. }
  321. retrieveTime += time.Now().Sub(nowTime)
  322. log.Printf("retrieveTime=%v", retrieveTime.String())
  323. }