Guest User

Bugg

a guest
Mar 8th, 2016
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Nim 6.17 KB | None | 0 0
  1. import math, options
  2.  
  3. const
  4.   MAX_ITEMS = 16
  5.   MAX_SPLIT = byte(4)
  6.  
  7. type
  8.   Bounds*   = tuple[x, y, w, h: float32]
  9.  
  10.   Region = object
  11.     bounds:   Bounds
  12.     regions:  array[4, int]
  13.     nodes:    int
  14.     size:     int
  15.     grade:    byte
  16.    
  17.   Node[T]   = object
  18.     case big: bool:
  19.     of false: sNodes: array[MAX_ITEMS, (Bounds, T)]
  20.     of true:  bNodes: seq[(Bounds, T)]
  21.  
  22.   QT* [T] = object
  23.     bounds:       Bounds
  24.     regions:      seq[Region]
  25.     nodes:        seq[Node[T]]
  26.     freeRegions:  seq[int]
  27.     freeNodes:    seq[int]
  28.    
  29.     itSeq:        seq[(Bounds, T)]
  30.  
  31. proc collides*(r1, r2: Bounds): bool =
  32.   r1.x < r2.x + r2.w and
  33.     r1.x + r1.w > r2.x and
  34.     r1.y < r2.y + r2.h and
  35.     r1.h + r1.y > r2.y
  36.      
  37. proc emptyRegions(region: Region): bool = region.regions[0] == -1
  38. proc emptyNodes(region: Region): bool = region.nodes == -1
  39.  
  40. iterator intersect(qt: QT, region: Region, b: Bounds): int =
  41.   if not emptyRegions(region):
  42.     for regionIndex in region.regions:
  43.       try:
  44.         if collides(qt.regions[regionIndex].bounds, b):
  45.           yield regionIndex
  46.       except:
  47.         quit "This would not happen generating garbage inside this iterator, Try appending 'let rrr = $regionIndex'"
  48.  
  49. proc newRegion(bounds: Bounds, grade: byte): Region =
  50.   Region(bounds: bounds, nodes: -1, regions: [-1, -1, -1, -1], grade: grade)
  51.  
  52. proc nextRegionsIndex(qt: var QT): array[4, int] =
  53.   if len(qt.freeRegions) > 0:
  54.     let firstRegion = qt.freeRegions[0]
  55.     del qt.freeRegions, 0
  56.     for i in 0..<4:
  57.       result[i] = firstRegion + i
  58.   else:
  59.     let i = len(qt.regions)
  60.     result = [i, i + 1, i + 2, i + 3]
  61.     setLen(qt.regions, i + 4)
  62.  
  63. proc nextNodeIndex(qt: var QT): int =
  64.   if len(qt.freeNodes) > 0:
  65.     result = qt.freeNodes[^1]
  66.     delete qt.freeNodes, len(qt.freeNodes) - 1
  67.   else:
  68.     result = len(qt.nodes)
  69.     setLen(qt.nodes, len(qt.nodes) + 1)
  70.  
  71. proc removeNode(qt: var QT, regionIndex: int, nodeIndex: int) =
  72.   add qt.freeNodes, nodeIndex
  73.   qt.regions[regionIndex].nodes = -1
  74.  
  75. proc splitRegion(qt: var QT, regionIndex: int, bA, bB, bC, bD: Bounds) =
  76.   let
  77.     grade = qt.regions[regionIndex].grade + 1
  78.     regions = nextRegionsIndex(qt)
  79.  
  80.   for i, bounds in [bA, bB, bC, bD]:
  81.     qt.regions[regions[i]] = newRegion(bounds, grade)
  82.    
  83.   qt.regions[regionIndex].regions = regions
  84.   qt.regions[regionIndex].size = 0
  85.  
  86. proc newNode[T](qt: var QT, regionIndex: int, node: Node[T]) =
  87.   let nodeIndex = nextNodeIndex(qt)
  88.   qt.nodes[nodeIndex] = node
  89.   qt.regions[regionIndex].nodes = nodeIndex
  90.  
  91. proc insert[T](qt: var QT[T], regionIdx: int, element: T, bounds: Bounds): bool =
  92.   let r = qt.regions[regionIdx]
  93.  
  94.   if r.nodes == -1:
  95.     newNode(qt, regionIdx, Node[T](big: false))
  96.     return insert(qt, regionIdx, element, bounds)
  97.  
  98.   let
  99.     nodesIndex  = qt.regions[regionIdx].nodes
  100.     size        = r.size
  101.    
  102.   template cNode(): auto = qt.nodes[nodesIndex]
  103.  
  104.   if size >= MAX_ITEMS and not cNode().big:
  105.     qt.nodes[nodesIndex] = Node[T](big: true, bNodes: @(cNode().sNodes))
  106.     return insert(qt, regionIdx, element, bounds)
  107.    
  108.   if cNode().big: add qt.nodes[nodesIndex].bNodes, (bounds, element)
  109.   else: cNode().sNodes[size] = (bounds, element)
  110.      
  111.   inc qt.regions[regionIdx].size
  112.   return true
  113.  
  114. proc createQuadTree*[T](bounds: Bounds): QT[T] =
  115.   QT[T](bounds: bounds, regions: @[newRegion(bounds, 0)], nodes: @[],
  116.     freeRegions: @[], freeNodes: @[])
  117.  
  118. proc add[T](qt: var QT[T], regionIndex: int, element: T, bounds: Bounds): bool =
  119.   let
  120.     region          = qt.regions[regionIndex]
  121.     hasRegions      = not emptyRegions(region)
  122.     full            = not hasRegions and region.size >= MAX_ITEMS
  123.     smallestRegion  = region.grade >= MAX_SPLIT
  124.     collision       = collides(bounds, region.bounds)
  125.  
  126.   # NOT HERE
  127.   if not collision: return false
  128.  
  129.   # CONTINUE INSERTING
  130.   if hasRegions:
  131.     for rIdx in intersect(qt, region, bounds):
  132.       if add(qt, rIdx, element, bounds): result = true
  133.     return
  134.    
  135.   # INSERT HERE
  136.   if smallestRegion or not full:
  137.     return insert(qt, regionIndex, element, bounds)
  138.    
  139.   # ADD MORE SPACE
  140.   let
  141.     nodesIdx          = region.nodes
  142.     nB                = region.bounds
  143.     (nW, nH, nX, nY)  = (nB.w / 2, nB.h / 2, nB.x, nB. y)
  144.    
  145.     bA = (nX      , nY      , nW, nH)
  146.     bB = (nX + nW , nY + nH , nW, nH)
  147.     bC = (nX      , nY + nH , nW, nH)
  148.     bD = (nX + nW , nY      , nW, nH)
  149.  
  150.   splitRegion qt, regionIndex, bA, bB, bC, bD
  151.  
  152.   ## Insert elements in new nodes
  153.   template cNode(): auto = qt.nodes[nodesIdx]
  154.  
  155.   for rIdx in qt.regions[regionIndex].regions:
  156.     if add(qt, rIdx, element, bounds): result = true
  157.     if cNode().big:
  158.       for n in cNode().bNodes:
  159.         discard add(qt, rIdx, n[1], n[0])
  160.     else:
  161.       for i, n in cNode().sNodes:
  162.         if i == region.size: break
  163.         discard add(qt, rIdx, n[1], n[0])
  164.  
  165.   removeNode qt, regionIndex, nodesIdx
  166.  
  167. proc add*[T](qt: var QT[T], bounds: Bounds, element: T): bool =
  168.   add(qt, 0, element, bounds)
  169.  
  170. #################################################
  171. ###############     TEST    #####################
  172. #################################################
  173.  
  174. block:
  175.   randomize(1234)
  176.   var qt = createQuadTree[string]((0f, 0f, 256f, 256f))
  177.  
  178.   # Split Regions
  179.   echo "INSERT MANY"
  180.   const TOTAL_INSERT = 64
  181.   for i in 1..TOTAL_INSERT:
  182.     let x, y = float32(random(256))
  183.     let (w, h) = (20f, 20f)
  184.    
  185.     doAssert add(qt, (x, y, w, h), $i)
  186.  
  187.   # >>> WITHOUT TOUCHING ANYTHING I GET
  188.   #
  189.   # # INSERT MANY
  190.   # # Traceback (most recent call last)
  191.   # # test2.nim(212)           test2
  192.   # # test2.nim(170)           add
  193.   # # test2.nim(45)            add
  194.   # # system.nim(2525)         sysFatal
  195.   # # Error: unhandled exception: index out of bounds [IndexError]
  196.  
  197.   # >>> NOW, MODIFY LINE 45 AND ADD 'let rrr = $regionIndex':
  198.   #
  199.   # # iterator intersect(qt: QT, region: Region, b: Bounds): int =
  200.     # # if not emptyRegions(region):
  201.       # # for regionIndex in region.regions:
  202.         # # let rrr = $regionIndex  # <<<------------------------------ LINE 43
  203.         # # if collides(qt.regions[regionIndex].bounds, b):
  204.           # # yield regionIndex
  205.  
  206.   # NO PROBLEM!!!
Advertisement
Add Comment
Please, Sign In to add comment