diff --git a/fcore/quad-tree-fold.sml b/fcore/quad-tree-fold.sml index a3735bb..7ccd7b8 100644 --- a/fcore/quad-tree-fold.sml +++ b/fcore/quad-tree-fold.sml @@ -71,12 +71,9 @@ struct , tree: QuadTreeType.t ) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => + NODE {topLeft, topRight, bottomLeft, bottomRight} => let (* fold over intersecting elements in this vector first *) - val state = foldVec - (itemX, itemY, itemW, itemH, 0, elements, state, env) - val halfW = quadW div 2 val halfH = quadH div 2 diff --git a/fcore/quad-tree-type.sml b/fcore/quad-tree-type.sml index d0166fa..4c442db 100644 --- a/fcore/quad-tree-type.sml +++ b/fcore/quad-tree-type.sml @@ -3,13 +3,7 @@ sig type item = {itemID: int, startX: int, startY: int, width: int, height: int} datatype t = - NODE of - { topLeft: t - , topRight: t - , bottomLeft: t - , bottomRight: t - , elements: item vector - } + NODE of {topLeft: t, topRight: t, bottomLeft: t, bottomRight: t} | LEAF of item vector datatype quadrant = @@ -25,13 +19,7 @@ struct type item = {itemID: int, startX: int, startY: int, width: int, height: int} datatype t = - NODE of - { topLeft: t - , topRight: t - , bottomLeft: t - , bottomRight: t - , elements: item vector - } + NODE of {topLeft: t, topRight: t, bottomLeft: t, bottomRight: t} | LEAF of item vector datatype quadrant = diff --git a/fcore/quad-tree.sml b/fcore/quad-tree.sml index eb8bbd0..9371635 100644 --- a/fcore/quad-tree.sml +++ b/fcore/quad-tree.sml @@ -92,19 +92,19 @@ struct } fun itemToString {itemID, startX, startY, width, height} = - String.concat [ - "{itemID = ", - Int.toString itemID, - ", startX = ", - Int.toString startX, - ", startY = ", - Int.toString startY, - ", width = ", - Int.toString width, - ", height = ", - Int.toString height, - "}" - ] + String.concat + [ "{itemID = " + , Int.toString itemID + , ", startX = " + , Int.toString startX + , ", startY = " + , Int.toString startY + , ", width = " + , Int.toString width + , ", height = " + , Int.toString height + , "}" + ] type t = QuadTreeType.t @@ -186,44 +186,40 @@ struct else PARENT_QUADRANT end - fun splitLeaf (qX, qY, qW, qH, tl, tr, bl, br, pe, elements, pos) = + fun splitLeaf (qX, qY, qW, qH, tl, tr, bl, br, elements, pos) = if pos < 0 then let val tl = Vector.fromList tl val tr = Vector.fromList tr val bl = Vector.fromList bl val br = Vector.fromList br - val pe = Vector.fromList pe in NODE { topLeft = LEAF tl , topRight = LEAF tr , bottomLeft = LEAF bl , bottomRight = LEAF br - , elements = pe } end else let val item = Vector.sub (elements, pos) val {startX = iX, startY = iY, width = iW, height = iH, ...} = item + + val vtl = visitTopLeft (iX, iY, iW, iH, qX, qY, qW, qH) + val vtr = visitTopRight (iX, iY, iW, iH, qX, qY, qW, qH) + val vbl = visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH) + val vbr = visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH) + + val tl = if vtl then item :: tl else tl + + val tr = if vtr then item :: tr else tr + + val bl = if vbl then item :: bl else bl + + val br = if vbr then item :: br else br in - case whichQuadrant (iX, iY, iW, iH, qX, qY, qW, qH) of - TOP_LEFT => - splitLeaf - (qX, qY, qW, qH, item :: tl, tr, bl, br, pe, elements, pos - 1) - | TOP_RIGHT => - splitLeaf - (qX, qY, qW, qH, tl, item :: tr, bl, br, pe, elements, pos - 1) - | BOTTOM_LEFT => - splitLeaf - (qX, qY, qW, qH, tl, tr, item :: bl, br, pe, elements, pos - 1) - | BOTTOM_RIGHT => - splitLeaf - (qX, qY, qW, qH, tl, tr, bl, item :: br, pe, elements, pos - 1) - | PARENT_QUADRANT => - splitLeaf - (qX, qY, qW, qH, tl, tr, bl, br, item :: pe, elements, pos - 1) + splitLeaf (qX, qY, qW, qH, tl, tr, bl, br, elements, pos - 1) end fun insert @@ -239,238 +235,110 @@ struct , tree: t ) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => - (* check which quadrant item is in, if any. - * If in any child quadrants, recurse insertion into there. - * Else, add to elements vector in current node. *) - (case - whichQuadrant - ( itemX - , itemY - , itemWidth - , itemHeight - , quadX - , quadY - , quadWidth - , quadHeight - ) - of - TOP_LEFT => - let - (* I know I am repeating myself by recalculating - * halfWidth/halfHeight in case branches but I prefer this - * over increating the indentation level further - * *) - val halfWidth = quadWidth div 2 - val halfHeight = quadHeight div 2 + NODE {topLeft, topRight, bottomLeft, bottomRight} => + let + val halfW = quadWidth div 2 + val halfH = quadHeight div 2 - val newTopLeft = insert - ( itemX - , itemY - , itemWidth - , itemHeight - , quadX - , quadY - , halfWidth - , halfHeight - , itemID - , topLeft - ) - in - NODE - { topLeft = newTopLeft - , topRight = topRight - , bottomLeft = bottomLeft - , bottomRight = bottomRight - , elements = elements - } - end - | TOP_RIGHT => - let - val halfWidth = quadWidth div 2 - val halfHeight = quadHeight div 2 - val middleX = quadX + halfWidth + val midX = halfW + quadX + val midY = halfH + quadY - val newTopRight = insert - ( itemX - , itemY - , itemWidth - , itemHeight - , middleX - , quadY - , halfWidth - , halfHeight - , itemID - , topRight - ) - in - NODE - { topLeft = topLeft - , topRight = newTopRight - , bottomLeft = bottomLeft - , bottomRight = bottomRight - , elements = elements - } - end - | BOTTOM_LEFT => - let - val halfWidth = quadWidth div 2 - val halfHeight = quadHeight div 2 - val middleY = quadY + halfHeight + val iX = itemX + val iY = itemY + val iW = itemWidth + val iH = itemHeight - val newBottomLeft = insert - ( itemX - , itemY - , itemWidth - , itemHeight - , quadX - , middleY - , halfWidth - , halfHeight - , itemID - , bottomLeft - ) - in - NODE - { topLeft = topLeft - , topRight = topRight - , bottomLeft = newBottomLeft - , bottomRight = bottomRight - , elements = elements - } - end - | BOTTOM_RIGHT => - let - val halfWidth = quadWidth div 2 - val halfHeight = quadHeight div 2 - val middleX = quadX + halfWidth - val middleY = quadY + halfHeight + val qX = quadX + val qY = quadY + val qW = quadWidth + val qH = quadHeight - val newBottomRight = insert - ( itemX - , itemY - , itemWidth - , itemHeight - , middleX - , middleY - , halfWidth - , halfHeight - , itemID - , bottomRight - ) - in - NODE - { topLeft = topLeft - , topRight = topRight - , bottomLeft = bottomLeft - , bottomRight = newBottomRight - , elements = elements - } - end - | PARENT_QUADRANT => - (* Does not fit in any of the child quadrants - * so we must add to the current parent quadrant. *) - let - val item = mkItem (itemID, itemX, itemY, itemWidth, itemHeight) - val elements = Vector.concat [elements, Vector.fromList [item]] - in - NODE - { topLeft = topLeft - , topRight = topRight - , bottomLeft = bottomLeft - , bottomRight = bottomRight - , elements = elements - } - end) + val vtl = visitTopLeft (iX, iY, iW, iH, qX, qY, qW, qH) + val vtr = visitTopRight (iX, iY, iW, iH, qX, qY, qW, qH) + val vbl = visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH) + val vbr = visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH) + + val tl = + if vtl then + insert (iX, iY, iW, iH, qX, qY, halfW, halfH, itemID, topLeft) + else + topLeft + + val tr = + if vtr then + insert (iX, iY, iW, iH, midX, qY, halfW, halfH, itemID, topRight) + else + topRight + + val bl = + if vbl then + insert + (iX, iY, iW, iH, qX, midY, halfW, halfH, itemID, bottomLeft) + else + bottomLeft + + val br = + if vbr then + insert + (iX, iY, iW, iH, midX, midY, halfW, halfH, itemID, bottomRight) + else + bottomRight + in + NODE {topLeft = tl, topRight = tr, bottomLeft = bl, bottomRight = br} + end | LEAF elements => if Vector.length elements + 1 > maxSize then (* have to calculate quadrants and split *) let val pos = Vector.length elements - 1 val item = mkItem (itemID, itemX, itemY, itemWidth, itemHeight) + + val halfW = quadWidth div 2 + val halfH = quadHeight div 2 + + val midX = halfW + quadX + val midY = halfH + quadY + + val iX = itemX + val iY = itemY + val iW = itemWidth + val iH = itemHeight + + val qX = quadX + val qY = quadY + val qW = quadWidth + val qH = quadHeight + + val vtl = visitTopLeft (iX, iY, iW, iH, qX, qY, qW, qH) + val vtr = visitTopRight (iX, iY, iW, iH, qX, qY, qW, qH) + val vbl = visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH) + val vbr = visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH) + + val pos = Vector.length elements - 1 + val item = mkItem (itemID, itemX, itemY, itemWidth, itemHeight) + + val tl = if vtl then [item] else [] + + val tr = if vtr then [item] else [] + + val bl = if vbl then [item] else [] + + val br = if vbr then [item] else [] + + val pe = [] in - (case - whichQuadrant - ( itemX - , itemY - , itemWidth - , itemHeight - , quadX - , quadY - , quadWidth - , quadHeight - ) - of - TOP_LEFT => - splitLeaf - ( quadX - , quadY - , quadWidth - , quadHeight - , [item] - , [] - , [] - , [] - , [] - , elements - , pos - ) - | TOP_RIGHT => - splitLeaf - ( quadX - , quadY - , quadWidth - , quadHeight - , [] - , [item] - , [] - , [] - , [] - , elements - , pos - ) - | BOTTOM_LEFT => - splitLeaf - ( quadX - , quadY - , quadWidth - , quadHeight - , [] - , [] - , [item] - , [] - , [] - , elements - , pos - ) - | BOTTOM_RIGHT => - splitLeaf - ( quadX - , quadY - , quadWidth - , quadHeight - , [] - , [] - , [] - , [item] - , [] - , elements - , pos - ) - | PARENT_QUADRANT => - splitLeaf - ( quadX - , quadY - , quadWidth - , quadHeight - , [] - , [] - , [] - , [] - , [item] - , elements - , pos - )) + splitLeaf + ( quadX + , quadY + , quadWidth + , quadHeight + , tl + , tr + , bl + , br + , elements + , pos + ) end else (* can insert itemID in elements vector *) @@ -502,9 +370,8 @@ struct val endX = startX + width val endY = startY + height in - isBetween (iX, startX, itemEndX, endX) andalso - isBetween (iY, startY, itemEndY, endY) andalso - itemID <> checkID + isBetween (iX, startX, itemEndX, endX) + andalso isBetween (iY, startY, itemEndY, endY) andalso itemID <> checkID end fun getCollisionsVec (iX, iY, iW, iH, itemID, pos, elements, acc) = @@ -522,9 +389,8 @@ struct fun getCollisionsAll (iX, iY, iW, iH, qW, qH, itemID, acc, tree) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => + NODE {topLeft, topRight, bottomLeft, bottomRight} => let - val acc = getCollisionsVec (iX, iY, iW, iH, itemID, 0, elements, acc) val halfWidth = qW div 2 val halfHeight = qH div 2 @@ -557,12 +423,8 @@ struct , tree: t ) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => + NODE {topLeft, topRight, bottomLeft, bottomRight} => let - (* get colliding elements in this node first *) - val acc = getCollisionsVec - (itemX, itemY, itemWidth, itemHeight, itemID, 0, elements, acc) - val halfW = quadWidth div 2 val halfH = quadHeight div 2 @@ -584,29 +446,55 @@ struct val vbl = visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH) val vbr = visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH) - val acc = + val acc = if vtl then helpGetCollisions (iX, iY, iW, iH, qX, qY, halfW, halfH, itemID, acc, topLeft) - else acc + else + acc - val acc = + val acc = if vtr then helpGetCollisions (iX, iY, iW, iH, midX, qY, halfW, halfH, itemID, acc, topRight) - else acc + else + acc - val acc = + val acc = if vbl then helpGetCollisions - (iX, iY, iW, iH, qX, midY, halfW, halfH, itemID, acc, bottomLeft) - else acc + ( iX + , iY + , iW + , iH + , qX + , midY + , halfW + , halfH + , itemID + , acc + , bottomLeft + ) + else + acc - val acc = + val acc = if vbl then helpGetCollisions - (iX, iY, iW, iH, midX, midY, halfW, halfH, itemID, acc, bottomRight) - else acc + ( iX + , iY + , iW + , iH + , midX + , midY + , halfW + , halfH + , itemID + , acc + , bottomRight + ) + else + acc in acc end @@ -710,10 +598,8 @@ struct fun getCollisionSidesAll (iX, iY, iW, iH, qW, qH, itemID, acc, tree) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => + NODE {topLeft, topRight, bottomLeft, bottomRight} => let - val acc = getCollisionSideVec - (iX, iY, iW, iH, itemID, 0, elements, acc) val halfWidth = qW div 2 val halfHeight = qH div 2 @@ -746,12 +632,8 @@ struct , tree: t ) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => + NODE {topLeft, topRight, bottomLeft, bottomRight} => let - (* get colliding elements in this node first *) - val acc = getCollisionSideVec - (itemX, itemY, itemWidth, itemHeight, itemID, 0, elements, acc) - val halfW = quadWidth div 2 val halfH = quadHeight div 2 @@ -773,29 +655,55 @@ struct val vbl = visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH) val vbr = visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH) - val acc = + val acc = if vtl then helpGetCollisionSides (iX, iY, iW, iH, qX, qY, halfW, halfH, itemID, acc, topLeft) - else acc + else + acc - val acc = + val acc = if vtr then helpGetCollisionSides (iX, iY, iW, iH, midX, qY, halfW, halfH, itemID, acc, topRight) - else acc + else + acc - val acc = + val acc = if vbl then helpGetCollisionSides - (iX, iY, iW, iH, qX, midY, halfW, halfH, itemID, acc, bottomLeft) - else acc + ( iX + , iY + , iW + , iH + , qX + , midY + , halfW + , halfH + , itemID + , acc + , bottomLeft + ) + else + acc - val acc = + val acc = if vbl then helpGetCollisionSides - (iX, iY, iW, iH, midX, midY, halfW, halfH, itemID, acc, bottomRight) - else acc + ( iX + , iY + , iW + , iH + , midX + , midY + , halfW + , halfH + , itemID + , acc + , bottomRight + ) + else + acc in acc end @@ -851,10 +759,8 @@ struct fun getCollisionsBelowAll (iX, iY, iW, iH, qW, qH, itemID, acc, tree) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => + NODE {topLeft, topRight, bottomLeft, bottomRight} => let - val acc = getCollisionsBelowVec - (iX, iY, iW, iH, itemID, 0, elements, acc) val halfWidth = qW div 2 val halfHeight = qH div 2 @@ -887,12 +793,8 @@ struct , tree: t ) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => + NODE {topLeft, topRight, bottomLeft, bottomRight} => let - (* get colliding elements in this node first *) - val acc = getCollisionsBelowVec - (itemX, itemY, itemWidth, itemHeight, itemID, 0, elements, acc) - val halfW = quadWidth div 2 val halfH = quadHeight div 2 @@ -914,29 +816,55 @@ struct val vbl = visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH) val vbr = visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH) - val acc = + val acc = if vtl then helpGetCollisionsBelow (iX, iY, iW, iH, qX, qY, halfW, halfH, itemID, acc, topLeft) - else acc + else + acc - val acc = + val acc = if vtr then helpGetCollisionsBelow (iX, iY, iW, iH, midX, qY, halfW, halfH, itemID, acc, topRight) - else acc + else + acc - val acc = + val acc = if vbl then helpGetCollisionsBelow - (iX, iY, iW, iH, qX, midY, halfW, halfH, itemID, acc, bottomLeft) - else acc + ( iX + , iY + , iW + , iH + , qX + , midY + , halfW + , halfH + , itemID + , acc + , bottomLeft + ) + else + acc - val acc = + val acc = if vbl then helpGetCollisionsBelow - (iX, iY, iW, iH, midX, midY, halfW, halfH, itemID, acc, bottomRight) - else acc + ( iX + , iY + , iW + , iH + , midX + , midY + , halfW + , halfH + , itemID + , acc + , bottomRight + ) + else + acc in acc end @@ -977,16 +905,15 @@ struct let val item = Vector.sub (elements, pos) in - if - isColliding (iX, iY, iW, iH, itemID, item) - then - let val _ = print ("quad-tree.sml: has collision: \n" ^ itemToString - item ^ "\n") + if isColliding (iX, iY, iW, iH, itemID, item) then + let + val _ = print + ("quad-tree.sml: has collision: \n" ^ itemToString item ^ "\n") in true end else - hasCollisionAtVec (iX, iY, iW, iH, itemID, pos + 1, elements) + hasCollisionAtVec (iX, iY, iW, iH, itemID, pos + 1, elements) end fun hasCollisionAt @@ -1002,10 +929,7 @@ struct , tree ) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => - hasCollisionAtVec - (itemX, itemY, itemWidth, itemHeight, itemID, 0, elements) - orelse + NODE {topLeft, topRight, bottomLeft, bottomRight} => let val halfW = quadWidth div 2 val halfH = quadHeight div 2 @@ -1028,29 +952,33 @@ struct val vbl = visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH) val vbr = visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH) - val tl = + val tl = if vtl then hasCollisionAt (iX, iY, iW, iH, qX, qY, halfW, halfH, itemID, topLeft) - else false + else + false - val tr = + val tr = if vtr then hasCollisionAt (iX, iY, iW, iH, midX, qY, halfW, halfH, itemID, topRight) - else false + else + false - val bl = + val bl = if vbl then hasCollisionAt (iX, iY, iW, iH, qX, midY, halfW, halfH, itemID, bottomLeft) - else false + else + false - val br = + val br = if vbl then hasCollisionAt (iX, iY, iW, iH, midX, midY, halfW, halfH, itemID, bottomRight) - else false + else + false in tl orelse tr orelse bl orelse br end @@ -1071,10 +999,8 @@ struct fun getItemID (itemX, itemY, itemW, itemH, quadX, quadY, quadW, quadH, tree) = case tree of - NODE {topLeft, topRight, bottomLeft, bottomRight, elements} => + NODE {topLeft, topRight, bottomLeft, bottomRight} => let - val tryID = getItemIDVec (itemX, itemY, itemW, itemH, 0, elements) - val halfW = quadW div 2 val halfH = quadH div 2 @@ -1096,31 +1022,37 @@ struct val vbl = visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH) val vbr = visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH) - val tryID = - if vtl andalso tryID = ~1 then - getItemID - (iX, iY, iW, iH, qX, qY, halfW, halfH, topLeft) - else tryID + val try1 = + if vtl then + getItemID (iX, iY, iW, iH, qX, qY, halfW, halfH, topLeft) + else + ~1 - val tryID = - if vtr andalso tryID = ~1 then - getItemID - (iX, iY, iW, iH, midX, qY, halfW, halfH, topRight) - else tryID + val try2 = + if vtr then + getItemID (iX, iY, iW, iH, midX, qY, halfW, halfH, topRight) + else + ~1 - val tryID = - if vbl andalso tryID = ~1 then - getItemID - (iX, iY, iW, iH, qX, midY, halfW, halfH, bottomLeft) - else tryID + val try3 = + if vbl then + getItemID (iX, iY, iW, iH, qX, midY, halfW, halfH, bottomLeft) + else + ~1 - val tryID = - if vbl andalso tryID <> ~1 then - getItemID - (iX, iY, iW, iH, midX, midY, halfW, halfH, bottomRight) - else tryID + val try4 = + if vbl then + getItemID (iX, iY, iW, iH, midX, midY, halfW, halfH, bottomRight) + else + ~1 + + (* get max: we assume query was narrow enough + * that only one ID is valid *) + val a = Int.max (try1, try2) + val a = Int.max (a, try3) + val a = Int.max (a, try4) in - tryID + a end | LEAF elements => getItemIDVec (itemX, itemY, itemW, itemH, 0, elements) end