rewrite merge functions which query grid instead of quad tree (only partially)

This commit is contained in:
2025-07-08 03:07:24 +01:00
parent aca7acc232
commit 12cfe7a04e
2 changed files with 110 additions and 291 deletions

BIN
dotscape

Binary file not shown.

View File

@@ -92,6 +92,8 @@ struct
foldr (f, left, acc) foldr (f, left, acc)
end end
| LEAF => acc | LEAF => acc
fun toList tree = foldr (fn (item, acc) => item ::acc, tree, [])
end end
structure CollisionTree = structure CollisionTree =
@@ -187,63 +189,61 @@ struct
) )
end) end)
local
fun getClickPoint (clickPoints, pos) = fun getClickPoint (clickPoints, pos) =
let val idx = Int.min (pos, Vector.length clickPoints - 1) let val idx = Int.min (pos, Vector.length clickPoints - 1)
in Vector.sub (clickPoints, idx) in Vector.sub (clickPoints, idx)
end end
fun folder fun folder
( windowWidth ( windowWidth
, windowHeight , windowHeight
, canvasWidth , canvasWidth
, canvasHeight , canvasHeight
, xClickPoints , xClickPoints
, yClickPoints , yClickPoints
) ({x, ex, y, ey, data}, acc) = ) ({x, ex, y, ey, data}, acc) =
let let
val ex = if ex = x then x + 1 else ex val ex = if ex = x then x + 1 else ex
val ey = if ey = y then y + 1 else ey val ey = if ey = y then y + 1 else ey
val x = getClickPoint (xClickPoints, x) val x = getClickPoint (xClickPoints, x)
val y = getClickPoint (yClickPoints, y) val y = getClickPoint (yClickPoints, y)
val ex = getClickPoint (xClickPoints, ex) val ex = getClickPoint (xClickPoints, ex)
val ey = getClickPoint (yClickPoints, ey) val ey = getClickPoint (yClickPoints, ey)
val startX = Ndc.fromPixelX (x, windowWidth, windowHeight) val startX = Ndc.fromPixelX (x, windowWidth, windowHeight)
val endX = Ndc.fromPixelX (ex, windowWidth, windowHeight) val endX = Ndc.fromPixelX (ex, windowWidth, windowHeight)
val startY = Ndc.fromPixelY (y, windowWidth, windowHeight) val startY = Ndc.fromPixelY (y, windowWidth, windowHeight)
val endY = Ndc.fromPixelY (ey, windowWidth, windowHeight) val endY = Ndc.fromPixelY (ey, windowWidth, windowHeight)
in in
Ndc.ltrbToVertexRgb (startX, startY, endX, endY, 0.0, 0.0, 0.0) :: acc Ndc.ltrbToVertexRgb (startX, startY, endX, endY, 0.0, 0.0, 0.0) :: acc
end end
in
fun toTriangles
( windowWidth
, windowHeight
, squares
, size
, canvasWidth
, canvasHeight
, xClickPoints
, yClickPoints
) =
let
val bintree = build (0, 0, size, squares, BinTree.empty)
val f = folder fun toTriangles
( windowWidth ( windowWidth
, windowHeight , windowHeight
, canvasWidth , squares
, canvasHeight , size
, xClickPoints , canvasWidth
, yClickPoints , canvasHeight
) , xClickPoints
val vec = BinTree.foldr (f, bintree, []) , yClickPoints
in ) =
Vector.concat vec let
end val bintree = build (0, 0, size, squares, BinTree.empty)
end
val f = folder
( windowWidth
, windowHeight
, canvasWidth
, canvasHeight
, xClickPoints
, yClickPoints
)
val vec = BinTree.foldr (f, bintree, [])
in
Vector.concat vec
end
(* building and querying quad tree, plus compression *) (* building and querying quad tree, plus compression *)
datatype quad_tree = datatype quad_tree =
@@ -251,6 +251,22 @@ struct
| NODE of {tl: quad_tree, tr: quad_tree, bl: quad_tree, br: quad_tree} | NODE of {tl: quad_tree, tr: quad_tree, bl: quad_tree, br: quad_tree}
| EMPTY | EMPTY
fun foldWithDuplicates (f, tree, acc) =
case tree of
EMPTY => acc
| LEAF item => f (item, acc)
| NODE {tl, tr, bl, br} =>
let
val acc = foldWithDuplicates (f, tl, acc)
val acc = foldWithDuplicates (f, tr, acc)
val acc = foldWithDuplicates (f, bl, acc)
in
foldWithDuplicates (f, br, acc)
end
fun toBintree qtree =
foldWithDuplicates (BinTree.insert, qtree, BinTree.empty)
fun buildTree (x, y, size, grid) = fun buildTree (x, y, size, grid) =
if x >= Vector.length grid orelse y >= Vector.length grid then if x >= Vector.length grid orelse y >= Vector.length grid then
EMPTY EMPTY
@@ -297,255 +313,58 @@ struct
NODE {tl = tl, tr = tr, bl = bl, br = br} NODE {tl = tl, tr = tr, bl = bl, br = br}
end) end)
fun getItemWithDataAt (x, y, qx, qy, size, tree, data) = local
case tree of fun loop (x, y, ex, ey, grid) =
EMPTY => NONE if x > 0 then
| LEAF (item as {x = ix, y = iy, ex = iex, ey = iey, data = oldData}) => if quadHasSameColour (x, y, ex, ey, grid) then
if (x >= ix andalso x <= iex) andalso (y >= iy andalso y <= iey) then loop (x - 1, y, x, ey, grid)
(* search coordinates are in item *) else
if data = oldData then (* data matches *) SOME item else NONE ex
else else
NONE 0
| NODE {tl, tr, bl, br} => in
if size mod 2 = 0 orelse size = 1 then fun getLeftmostX ({x, y, ex, ey, data}, grid) =
let loop (x - 1, y, x, ey, grid)
val halfSize = size div 2 end
val qmx = x + halfSize
val qfx = x + size local
val qmy = y + halfSize fun loop (x, y, ex, ey, grid) =
val qfy = y + size if x < Vector.length grid andalso ex < Vector.length grid then
in if quadHasSameColour (x, y, ex, ey, grid) then
if y >= qy andalso y <= qmy then loop (ex, y, ex + 1, ey, grid)
(* top *) else
if x >= qx andalso x <= qmx then x
(* top left *)
getItemWithDataAt (x, y, qx, qy, halfSize, tl, data)
else
(* top right *)
getItemWithDataAt (x, y, qx + halfSize, qy, halfSize, tr, data)
else (* bottom *) if x >= qx andalso x <= qmx then
(* bottom left *)
getItemWithDataAt (x, y, qx, qy + halfSize, halfSize, bl, data)
else
(* bottom right *)
getItemWithDataAt
(x, y, qx + halfSize, qy + halfSize, halfSize, br, data)
end
else else
let Vector.length grid - 1
val halfSizeBefore = size div 2
val halfSizeAfter = (size + 1) div 2
val qmx = x + halfSizeBefore
val qmy = y + halfSizeBefore
in
if y >= qy andalso y <= qmy then
(* top *)
if x >= qx andalso x <= qmx then
(* top left *)
getItemWithDataAt (x, y, qx, qy, halfSizeAfter, tl, data)
else
(* top right *)
getItemWithDataAt (x, y, qmx, qy, halfSizeAfter, tr, data)
else (* bottom *) if x >= qx andalso x <= qmx then
(* bottom left *)
getItemWithDataAt (x, y, qx, qmy, halfSizeAfter, bl, data)
else
(* bottom right *)
getItemWithDataAt (x, y, qmx, qmy, halfSizeAfter, br, data)
end
fun getLeftmostX (rootSize, rootTree, prevItem) =
let
val {x = prevX, y = prevY, ex = prevEx, ey = prevEy, data = prevData} =
prevItem
in in
case fun getRightmostX ({x, y, ex, ey, data}, grid) =
getItemWithDataAt (prevX - 1, prevY, 0, 0, rootSize, rootTree, prevData) loop (ex, y, ex + 1, ey, grid)
of end
SOME (newItem as {y = newY, ey = newEy, data = newData, ...}) =>
if prevY = newY andalso newEy = prevEy andalso prevData = newData then local
(* Y side has same edge, so is mergeable, and data is also same *) fun loop (x, y, ex, ey, grid) =
getLeftmostX (rootSize, rootTree, newItem) if y < 0 then 0
else
if quadHasSameColour (x, y, ex, ey, grid) then
loop (x, y - 1, ex, y, grid)
else else
prevX ey
| NONE => prevX in
fun getTopmostY ({x, y, ex, ey, data}, grid) =
loop (x, y - 1, ex, y, grid)
end end
fun getRightmostX (rootSize, rootTree, prevItem) = local
let fun loop (x, y, ex, ey, grid) =
val {x = prevX, y = prevY, ex = prevEx, ey = prevEy, data = prevData} = if y < Vector.length grid andalso ey < Vector.length grid then
prevItem if quadHasSameColour (x, y, ex, ey, grid) then
in loop (x, ey, ex, ey + 1, grid)
case
getItemWithDataAt
(prevEx + 1, prevY, 0, 0, rootSize, rootTree, prevData)
of
SOME (newItem as {y = newY, ey = newEy, data = newData, ...}) =>
if prevY = newY andalso newEy = prevEy andalso prevData = newData then
getRightmostX (rootSize, rootTree, newItem)
else else
prevEx y
| NONE => prevEx else
end Vector.length grid - 1
fun getTopmostY (rootSize, rootTree, prevItem) =
let
val {x = prevX, y = prevY, ex = prevEx, ey = prevEy, data = prevData} =
prevItem
in in
case fun getBottomY ({x, y, ex, ey, data}, grid) =
getItemWithDataAt (prevX, prevY - 1, 0, 0, rootSize, rootTree, prevData) loop (x, ey, ex, ey + 1, grid)
of
SOME (newItem as {x = newX, ex = newEx, data = newData, ...}) =>
if prevX = newX andalso prevEx = newEx andalso prevData = newData then
(* X side has same edge and data is also same, so mergeable *)
getTopmostY (rootSize, rootTree, newItem)
else
prevY
| NONE => prevY
end end
fun getBottomY (rootSize, rootTree, prevItem) =
let
val {x = prevX, y = prevY, ex = prevEx, ey = prevEy, data = prevData} =
prevItem
in
case
getItemWithDataAt
(prevX, prevEy + 1, 0, 0, rootSize, rootTree, prevData)
of
SOME (newItem as {x = newX, ex = newEx, data = newData, ...}) =>
if prevX = newX andalso prevEx = newEx andalso prevData = newData then
(* X side has same edge and data is also same, so mergeable *)
getBottomY (rootSize, rootTree, newItem)
else
prevEy
| NONE => prevEy
end
datatype merge_dir =
HORIZONTAL of {left: int, right: int}
| VERTICAL of {up: int, down: int}
| NO_MERGE
fun getHorizontal (ox, oex, leftX, rightX) =
let
val left = if leftX < ox then ox - 1 else ox
val right = if rightX > oex then oex + 1 else oex
in
HORIZONTAL {left = left, right = right}
end
fun getVertical (oy, oey, upY, bottomY) =
let
val up = if upY < oy then oy - 1 else oy
val down = if bottomY > oey then oey + 1 else oey
in
VERTICAL {up = up, down = down}
end
fun getMergeDir (rootSize, rootTree, item) =
let
val {x = ox, y = oy, ex = oex, ey = oey, data} = item
val leftX = getLeftmostX (rootSize, rootTree, item)
val rightX = getRightmostX (rootSize, rootTree, item)
val upY = getTopmostY (rootSize, rootTree, item)
val bottomY = getBottomY (rootSize, rootTree, item)
val xChanged = leftX <> ox orelse rightX <> oex
val yChanged = upY <> oy orelse bottomY <> oey
in
if xChanged andalso yChanged then
let
val diffX = abs (rightX - leftX)
val diffY = abs (bottomY - upY)
in
if diffX > diffY then getHorizontal (ox, oex, leftX, rightX)
else getVertical (oy, oey, upY, bottomY)
end
else if xChanged then
getHorizontal (ox, oex, leftX, rightX)
else if yChanged then
getVertical (oy, oey, upY, bottomY)
else
NO_MERGE
end
fun mergeHorizontal (item, newX, newEx) =
let val {y, ey, data, x = _, ex = _} = item
in {x = newX, ex = newEx, y = y, ey = ey, data = data}
end
fun mergeVertical (item, newY, newEy) =
let val {x, ex, data, y = _, ey = _} = item
in {y = newY, ey = newEy, x = x, ex = ex, data = data}
end
local
fun mergePass (qx, qy, size, tree, rootSize, rootTree) =
case tree of
EMPTY => (EMPTY, false)
| LEAF item =>
let
val mergeDir = getMergeDir (rootSize, rootTree, item)
in
case mergeDir of
NO_MERGE => (LEAF item, false)
| VERTICAL {up, down} =>
(LEAF (mergeVertical (item, up, down)), true)
| HORIZONTAL {left, right} =>
(LEAF (mergeHorizontal (item, left, right)), true)
end
| NODE {tl, tr, bl, br} =>
if size = 1 orelse size mod 2 = 0 then
let
val halfSize = size div 2
val (tl, didMergeTl) = mergePass
(qx, qy, halfSize, tl, rootSize, rootTree)
val (tr, didMergeTr) = mergePass
(qx + halfSize, qy, halfSize, tr, rootSize, rootTree)
val (bl, didMergeBl) = mergePass
(qx, qy + halfSize, halfSize, bl, rootSize, rootTree)
val (br, didMergeBr) = mergePass
(qx + halfSize, qy + halfSize, halfSize, br, rootSize, rootTree)
val didMergeAny =
didMergeTl orelse didMergeTr orelse didMergeBl orelse didMergeBr
val node = NODE {tl = tl, tr = tr, bl = bl, br = br}
in
(node, didMergeAny)
end
else
let
val halfSizeBefore = size div 2
val halfSizeAfter = (size + 1) div 2
val qmx = qx + halfSizeBefore
val qmy = qy + halfSizeAfter
val (tl, didMergeTl) = mergePass
(qx, qy, halfSizeAfter, tl, rootSize, rootTree)
val (tr, didMergeTr) = mergePass
(qmx, qy, halfSizeAfter, tr, rootSize, rootTree)
val (bl, didMergeBl) = mergePass
(qx, qmy, halfSizeAfter, bl, rootSize, rootTree)
val (br, didMergeBr) = mergePass
(qmx, qmy, halfSizeAfter, br, rootSize, rootTree)
val didMergeAny =
didMergeTl orelse didMergeTr orelse didMergeBl orelse didMergeBr
val node = NODE {tl = tl, tr = tr, bl = bl, br = br}
in
(node, didMergeAny)
end
in
fun merge (rootSize, rootTree) =
let
val (newTree, didMerge) = mergePass
(0, 0, rootSize, rootTree, rootSize, rootTree)
in
if didMerge then merge (rootSize, newTree) else newTree
end
end
end end