From b46201d4700dea6ba224433adcfda908d409f484 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 29 Jun 2020 09:51:30 +0200 Subject: [PATCH] adding quadtree file egan --- quadtree.go | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/quadtree.go b/quadtree.go index e69de29..75a3a8c 100644 --- a/quadtree.go +++ b/quadtree.go @@ -0,0 +1,174 @@ +package pixel + +import ( + "github.com/faiface/pixel" +) + +type Collidable interface { + GetRect() *pixel.Rect +} + +// i separated this data so i can simply copy it to subnodes +type Common struct { + Depth int // maximal level tree can reach + level int // this takes track of level + Cap int // max amount of objects per quadrant, if there is more quadrant splits +} + +type QuadTree struct { + pixel.Rect + Nodes []*QuadTree + Shapes []Collidable + Common +} + +// Creates new quad tree reference. +// bounds - defines position of quad tree and its size. If shapes goes out of bounds they +// will not be assigned to quadrants and the tree will be ineffective. +// depth - resolution of quad tree. It lavais splits in half so if bounds size is 100 x 100 +// and depth is 2 smallest quadrants will be 25 x 25. Making resolution too high is redundant +// if shapes cannot fit into smallest quadrants. +// cap - sets maximal capacity of quadrant before it splits to 4 smaller. Making can too big is +// inefficient. optimal value can be 10 but its allways better to test what works the best. +func NewQuadTree(bounds pixel.Rect, depth, cap int) *QuadTree { + return &QuadTree{ + Rect: bounds, + Common: Common{ + Depth: depth, + Cap: cap, + }, + } +} + +//generates subquadrants +func (q *QuadTree) split() { + q.Nodes = make([]*QuadTree, 4) + newCommon := q.Common + newCommon.Level++ + halfH := q.H() / 2 + halfW := q.W() / 2 + center := q.Center() + //top-left + q.Nodes[0] = &QuadTree{ + Rect: pixel.Rect{ + Min: pixel.V(q.Min.X, q.Min.Y+halfH), + Max: pixel.V(q.Max.X-halfW, q.Max.Y), + }, + Common: newCommon, + } + //top-right + q.Nodes[1] = &QuadTree{ + Rect: pixel.Rect{ + Min: center, + Max: q.Max, + }, + Common: newCommon, + } + //bottom-left + q.Nodes[2] = &QuadTree{ + Rect: pixel.Rect{ + Min: q.Min, + Max: center, + }, + Common: newCommon, + } + //bottom-right + q.Nodes[3] = &QuadTree{ + Rect: pixel.Rect{ + Min: pixel.V(q.Min.X+halfW, q.Min.Y), + Max: pixel.V(q.Max.X, q.Min.Y+halfH), + }, + Common: newCommon, + } +} + +// finds out to witch subquadrant the shape belongs to. Shape has to overlap only with one quadrant, +// otherwise it returns -1 +func (q *QuadTree) getSub(rect *pixel.Rect) int8 { + vertical := q.Min.X + q.W()/2 + horizontal := q.Min.Y + q.H()/2 + + if rect.Max.X < q.Min.X || rect.Max.X > q.Max.X || rect.Min.Y < q.Min.Y || rect.Max.Y > q.Max.Y { + return -1 + } + + left := rect.Max.X < vertical + right := rect.Min.X > vertical + if rect.Min.Y > horizontal { + // top + if left { + return 0 // left + } else if right { + return 1 // right + } + } else if rect.Max.Y < horizontal { + // bottom + if left { + return 2 // left + } else if right { + return 3 // right + } + } + return -1 +} + +// Adds the shape to quad tree and asians it to correct quadrant. +// Proper way is adding all shapes first and then detecting collisions. +// For struct to implement Collidable interface it has to have +// GetRect() *pixel.Rect defined. GetRect function also slightly affects performance. +func (q *QuadTree) Insert(collidable Collidable) { + rect := collidable.GetRect() + q.Shapes = append(q.Shapes, collidable) + if len(q.Nodes) != 0 { + i := q.getSub(rect) + if i != -1 { + q.Nodes[i].Insert(collidable) + } + return + } else if q.Cap == len(q.Shapes) && q.Level != q.Depth { + q.split() + for _, s := range q.Shapes { + i := q.getSub(s.GetRect()) + if i != -1 { + q.Nodes[i].Insert(s) + } + } + } +} + +// gets smallest generated quadrant that rect fits into +func (q *QuadTree) getQuad(rect *pixel.Rect) *QuadTree { + if len(q.Nodes) == 0 { + return q + } + subIdx := q.getSub(rect) + if subIdx == -1 { + return q + } + return q.Nodes[subIdx].getQuad(rect) +} + +// returns all collidables that this rect can possibly collide with +// thought it also returns the shape it self if it wos inserted +func (q *QuadTree) Retrieve(rect *pixel.Rect) []Collidable { + return q.getQuad(rect).Shapes +} + +// returns all coliding shapes +func (q *QuadTree) GetColliding(collidable Collidable) []Collidable { + var res []Collidable + rect := collidable.GetRect() + for _, c := range q.Retrieve(rect) { + if c.GetRect().Intersects(*rect) && c != collidable { + res = append(res, c) + } + } + return res +} + +// Resets the tree, use this every frame before inserting all shapes +// other wise you will run out of memory eventually and tree will not even work properly +func (q *QuadTree) Clear() { + q.Shapes = []Collidable{} + q.Nodes = []*QuadTree{} +}