Guest User

Buggy Bug

a guest
Mar 7th, 2016
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Nim 6.85 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.       if collides(qt.regions[regionIndex].bounds, b):
  44.         yield regionIndex
  45.  
  46. proc newRegion(bounds: Bounds, grade: byte): Region =
  47.   Region(bounds: bounds, nodes: -1, regions: [-1, -1, -1, -1], grade: grade)
  48.  
  49. proc nextRegionsIndex(qt: var QT): array[4, int] =
  50.   if len(qt.freeRegions) > 0:
  51.     let firstRegion = qt.freeRegions[0]
  52.     del qt.freeRegions, 0
  53.     for i in 0..<4:
  54.       result[i] = firstRegion + i
  55.   else:
  56.     let i = len(qt.regions)
  57.     result = [i, i + 1, i + 2, i + 3]
  58.     setLen(qt.regions, i + 4)
  59.  
  60. proc nextNodeIndex(qt: var QT): int =
  61.   if len(qt.freeNodes) > 0:
  62.     result = qt.freeNodes[^1]
  63.     delete qt.freeNodes, len(qt.freeNodes) - 1
  64.   else:
  65.     result = len(qt.nodes)
  66.     setLen(qt.nodes, len(qt.nodes) + 1)
  67.  
  68. proc removeNode(qt: var QT, regionIndex: int, nodeIndex: int) =
  69.   add qt.freeNodes, nodeIndex
  70.   qt.regions[regionIndex].nodes = -1
  71.  
  72. proc splitRegion(qt: var QT, regionIndex: int, bA, bB, bC, bD: Bounds) =
  73.   let
  74.     grade = qt.regions[regionIndex].grade + 1
  75.     regions = nextRegionsIndex(qt)
  76.  
  77.   for i, bounds in [bA, bB, bC, bD]:
  78.     qt.regions[regions[i]] = newRegion(bounds, grade)
  79.    
  80.   qt.regions[regionIndex].regions = regions
  81.   qt.regions[regionIndex].size = 0
  82.  
  83. proc newNode[T](qt: var QT, regionIndex: int, node: Node[T]) =
  84.   let nodeIndex = nextNodeIndex(qt)
  85.   qt.nodes[nodeIndex] = node
  86.   qt.regions[regionIndex].nodes = nodeIndex
  87.  
  88. proc insert[T](qt: var QT[T], regionIdx: int, element: T, bounds: Bounds): bool =
  89.   let r = qt.regions[regionIdx]
  90.  
  91.   if r.nodes == -1:
  92.     newNode(qt, regionIdx, Node[T](big: false))
  93.     return insert(qt, regionIdx, element, bounds)
  94.  
  95.   let
  96.     nodesIndex  = qt.regions[regionIdx].nodes
  97.     size        = r.size
  98.    
  99.   template cNode(): auto = qt.nodes[nodesIndex]
  100.  
  101.   if size >= MAX_ITEMS and not cNode().big:
  102.     qt.nodes[nodesIndex] = Node[T](big: true, bNodes: @(cNode().sNodes))
  103.     return insert(qt, regionIdx, element, bounds)
  104.    
  105.   if cNode().big: add qt.nodes[nodesIndex].bNodes, (bounds, element)
  106.   else: cNode().sNodes[size] = (bounds, element)
  107.      
  108.   inc qt.regions[regionIdx].size
  109.   return true
  110.  
  111. proc bounds*(qt: QT): Bounds = qt.bounds
  112.  
  113. proc createQuadTree*[T](bounds: Bounds): QT[T] =
  114.   QT[T](bounds: bounds, regions: @[newRegion(bounds, 0)], nodes: @[],
  115.     freeRegions: @[], freeNodes: @[])
  116.  
  117. proc add[T](qt: var QT[T], regionIndex: int, element: T, bounds: Bounds): bool =
  118.   let
  119.     region          = qt.regions[regionIndex]
  120.     hasRegions      = not emptyRegions(region)
  121.     full            = not hasRegions and region.size >= MAX_ITEMS
  122.     smallestRegion  = region.grade >= MAX_SPLIT
  123.     collision       = collides(bounds, region.bounds)
  124.  
  125.   # NOT HERE
  126.   if not collision: return false
  127.  
  128.   # CONTINUE INSERTING
  129.   if hasRegions:
  130.     for rIdx in intersect(qt, region, bounds):
  131.       if add(qt, rIdx, element, bounds): result = true
  132.     return
  133.    
  134.   # INSERT HERE
  135.   if smallestRegion or not full:
  136.     return insert(qt, regionIndex, element, bounds)
  137.    
  138.   # ADD MORE SPACE
  139.   let
  140.     nodesIdx          = region.nodes
  141.     nB                = region.bounds
  142.     (nW, nH, nX, nY)  = (nB.w / 2, nB.h / 2, nB.x, nB. y)
  143.    
  144.     bA = (nX      , nY      , nW, nH)
  145.     bB = (nX + nW , nY + nH , nW, nH)
  146.     bC = (nX      , nY + nH , nW, nH)
  147.     bD = (nX + nW , nY      , nW, nH)
  148.  
  149.   splitRegion qt, regionIndex, bA, bB, bC, bD
  150.  
  151.   ## Insert elements in new nodes
  152.   template cNode(): auto = qt.nodes[nodesIdx]
  153.  
  154.   for rIdx in qt.regions[regionIndex].regions:
  155.     if add(qt, rIdx, element, bounds): result = true
  156.     if cNode().big:
  157.       for n in cNode().bNodes:
  158.         discard add(qt, rIdx, n[1], n[0])
  159.     else:
  160.       for i, n in cNode().sNodes:
  161.         if i == region.size: break
  162.         discard add(qt, rIdx, n[1], n[0])
  163.  
  164.   removeNode qt, regionIndex, nodesIdx
  165.  
  166. proc add*[T](qt: var QT[T], bounds: Bounds, element: T): bool =
  167.   add(qt, 0, element, bounds)
  168.  
  169. proc internalTest[T](
  170.     qt:     QT[T],
  171.     region: Region,
  172.     list:   var seq[(Bounds, T)],
  173.     b:      Bounds) =
  174.  
  175.   if not collides(region.bounds, b):
  176.     return
  177.  
  178.   if not emptyNodes(region):
  179.     template cNode(): auto = qt.nodes[region.nodes]
  180.  
  181.     if cNode().big:
  182.       for n in cNode().bNodes:
  183.         if collides(n[0], b, qt.bounds) and n notin list:
  184.           add list, n
  185.     else:
  186.       for i, n in cNode().sNodes:
  187.         if i == region.size: break
  188.         if collides(n[0], b, qt.bounds) and n notin list:
  189.           add list, n
  190.     return
  191.  
  192.   for rIdx in intersect(qt, region, b):
  193.     internalTest qt, qt.regions[rIdx], list, b
  194.    
  195. proc test*[T](qt: QT[T], list: var seq[(Bounds, T)], b: Bounds) =
  196.   internalTest(qt, qt.regions[0], list, b)
  197.  
  198. #################################################
  199. ###############     TEST    #####################
  200. #################################################
  201.  
  202. block:
  203.   randomize(1234)
  204.   var qt = createQuadTree[string]((0f, 0f, 256f, 256f))
  205.  
  206.   # Split Regions
  207.   echo "INSERT MANY"
  208.   const TOTAL_INSERT = 64
  209.   for i in 1..TOTAL_INSERT:
  210.     let x, y = float32(random(256))
  211.     let (w, h) = (20f, 20f)
  212.    
  213.     doAssert add(qt, (x, y, w, h), $i), "NUMERO " & $i
  214.  
  215.   # >>> WITHOUT TOUCHING ANYTHING I GET
  216.   #
  217.   # # INSERT MANY
  218.   # # Traceback (most recent call last)
  219.   # # test2.nim(212)           test2
  220.   # # test2.nim(170)           add
  221.   # # test2.nim(45)            add
  222.   # # system.nim(2525)         sysFatal
  223.   # # Error: unhandled exception: index out of bounds [IndexError]
  224.  
  225.   # >>> NOW, MODIFY LINE 45 AND ADD 'let rrr = $regionIndex':
  226.   #
  227.   # # iterator intersect(qt: QT, region: Region, b: Bounds): int =
  228.     # # if not emptyRegions(region):
  229.       # # for regionIndex in region.regions:
  230.         # # let rrr = $regionIndex  # <<<------------------------------ LINE 45
  231.         # # if collides(qt.regions[regionIndex].bounds, b):
  232.           # # yield regionIndex
  233.  
  234.   # NO PROBLEM!!!
Add Comment
Please, Sign In to add comment