Skip to content

Commit

Permalink
[Builder] create quadtree from many small patches
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanmaierhofer committed Nov 15, 2023
1 parent 606710f commit 78cef35
Show file tree
Hide file tree
Showing 13 changed files with 546 additions and 138 deletions.
2 changes: 1 addition & 1 deletion src/Aardvark.Geometry.Quadtree.Tests/StructureTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module StructureTests =
Split : int
}

let createQuadtreeTyped<'a> def spec =
let createQuadtreeTyped<'a when 'a : equality> def spec =
let (w, h) = spec.Size
let mapping = DataMapping(spec.Origin, V2i(w,h))
let layer = Layer<'a>(def, spec.Data, mapping)
Expand Down
6 changes: 5 additions & 1 deletion src/Aardvark.Geometry.Quadtree.sln
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_", "_", "{CBD0D4B2-E888-4C
..\build.cmd = ..\build.cmd
..\build.sh = ..\build.sh
..\LICENSE = ..\LICENSE
..\.github\workflows\linux.yml = ..\.github\workflows\linux.yml
..\.github\workflows\mac.yml = ..\.github\workflows\mac.yml
..\paket.dependencies = ..\paket.dependencies
..\paket.lock = ..\paket.lock
..\.github\workflows\publish.yml = ..\.github\workflows\publish.yml
..\README.md = ..\README.md
..\RELEASE_NOTES.md = ..\RELEASE_NOTES.md
..\.github\workflows\windows.yml = ..\.github\workflows\windows.yml
EndProjectSection
EndProject
Global
Expand All @@ -43,7 +47,7 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E996B5E6-49B2-4479-9DDE-455A40509F43}
EnterpriseLibraryConfigurationToolBinariesPathV6 = packages\EnterpriseLibrary.TransientFaultHandling.6.0.1304.0\lib\portable-net45+win+wp8;packages\EnterpriseLibrary.TransientFaultHandling.Data.6.0.1304.1\lib\NET45
SolutionGuid = {E996B5E6-49B2-4479-9DDE-455A40509F43}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<Compile Include="Merge.fs" />
<Compile Include="Quadtree.fs" />
<Compile Include="Query.fs" />
<Compile Include="Builder.fs" />
<Compile Include="PrettyPrint.fs" />
</ItemGroup>
<ItemGroup>
Expand Down
144 changes: 144 additions & 0 deletions src/Aardvark.Geometry.Quadtree/Builder.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
namespace Aardvark.Geometry.Quadtree

open Aardvark.Base
open System
open System.Collections.Generic

module Builder =

let private debugOutput = false

let rec private build' (sampleExponent : int) (rootCell : Cell2d) (patches : LayerSet[]) =

if debugOutput then
printfn "[DEBUG] build' rootCell = %A, %d patches" rootCell patches.Length

match patches.Length with

| 0 -> NoNode

| 1 ->

let theSinglePatch = patches[0]

if debugOutput then
printfn "[DEBUG] SINGLE patch (%A)" theSinglePatch.SampleWindow

Quadtree.Build BuildConfig.Default theSinglePatch.Layers

| n -> // n patches

let ebb = patches |> Seq.map (fun patch -> patch.BoundingBox) |> Box2d

let rootBounds = getBoundsForExponent sampleExponent rootCell
if rootBounds.Size.X <= 256 && rootBounds.Size.Y <= 256 then

let bbWindow = patches |> Seq.map (fun patch -> patch.SampleWindow) |> Box2l

if debugOutput then
printfn "[DEBUG] MERGE %d patches; %A" n (bbWindow - rootBounds.Min)


//if debugOutput then
// for patch in patches do
// printfn "[DEBUG] %A (exact %A)" (patch.SampleWindow - rootBounds.Min) (bbWindow - rootBounds.Min)

let merged = LayerSet.Merge patches
let qnode = QNode(Guid.NewGuid(), ebb, rootCell, BuildConfig.Default.SplitLimitPowerOfTwo, merged)
let xs = qnode |> InMemoryNode |> Query.All Query.Config.Default |> Seq.map (fun x -> x.GetSamples<V4f>(Defs.HeightsBilinear4f)) |> Array.ofSeq |> Array.collect id
//for x in xs do printfn "%A" x
qnode |> InMemoryNode

else

let bbQuadrants = rootCell.Children |> Array.map (fun subCell -> subCell.GetBoundsForExponent(sampleExponent))

let patchesPerQuadrant =
rootCell.Children
|> Array.map (fun subCell ->
let bbQuadrant = subCell.GetBoundsForExponent(sampleExponent)
let subPatches = patches |> Array.choose (fun b -> b.WithWindow bbQuadrant)
(subCell, subPatches)
)


if debugOutput then
printfn "[DEBUG] SPLIT %d patches" n

//for i in 0..3 do
// let (subCell, subPatches) = patchesPerQuadrant[i]
// printfn "%A%A[%d] size = %A, %d patches" rootCell ebb i bbWindow.Size subPatches.Length

let subNodes = patchesPerQuadrant |> Array.map (fun (subCell, subPatches) ->
match subPatches.Length with
| 0 -> NoNode
| _ -> build' sampleExponent subCell subPatches
)

let result = { Id = Guid.NewGuid(); ExactBoundingBox = ebb; Cell = rootCell; SplitLimitExponent = BuildConfig.Default.SplitLimitPowerOfTwo; SubNodes = subNodes }
result |> InMemoryInner

/// Creates a quadtree from many small patches.
let Build (patches : LayerSet seq) : QNodeRef =
let patches = patches |> Array.ofSeq
let rootCell = patches |> Array.map (fun patch -> patch.BoundingBox) |> Box2d |> Cell2d
let sampleExponent = (patches |> Array.distinctBy (fun x -> x.SampleExponent) |> Array.exactlyOne).SampleExponent
build' sampleExponent rootCell patches


/// Creates a quadtree from many small patches.
type Builder () =

let l = obj()
let patches = Dictionary<int, List<LayerSet>>()
let mutable isEmpty = true
let mutable layerCount = 0

/// Add patch.
member this.Add (patch : LayerSet) : unit = lock l (fun () ->

if isEmpty then
layerCount <- patch.Layers.Length
isEmpty <- true

invariantm
(patch.Layers.Length = layerCount)
(fun()->sprintf "Expected %d layers, but patch has %d. layers" layerCount patch.Layers.Length)
"e76e1577-4963-441c-884c-dc32da0b3b15"

// get or create patch list for patch's resolution (a.k.a. sample exponent)
let list =
match patches.TryGetValue(patch.SampleExponent) with
| (true, xs) -> xs
| (false, _) -> let xs = List<LayerSet>()
patches[patch.SampleExponent] <- xs
xs
// add patch to list
list.Add(patch)
)

/// Add patch.
member this.Add (patch : ILayer) : unit =
this.Add(LayerSet([| patch |]))

/// Add patch.
member this.Add (patch : QNode) : unit =
this.Add(patch.LayerSet)

/// Build a quadtree from all the patches that have been added to this builder.
member this.Build () : QNodeRef option =

patches
// (1) sort per-resolution patch lists from coarse to fine resolution ...
|> Seq.sortByDescending (fun kv -> kv.Key) |> Seq.map (fun kv -> kv.Value)
// (2) create a quadtree for each resolution ...
|> Seq.map Builder.Build
// (3) merge quadtrees with finer resolution always dominating ...
|> Seq.fold (fun state item ->
match state with
| None -> Some item
| Some state -> Quadtree.Merge Dominance.SecondDominates state item |> Some
)
None // initial state


2 changes: 2 additions & 0 deletions src/Aardvark.Geometry.Quadtree/Defs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ module Defs =
let VolumesBilinear4d = def "9aadc102-68b7-4e6f-ba22-bad1440e1bcf" "Quadtree.VolumesBilinear4d" "Quadtree. Volume value (height difference) per sample as bilinear params. volume(x,y) = A + B*x + C*y+ D*x*y, where x,y in range [-sample.Size/2, +sample.Size/2], (x=0,y=0) corresponds to center of sample (A), and A=v.X, B=v.Y, C=v.Z, D=v.W. V4d[]." Durable.Aardvark.V4dArray
let VolumesBilinear4dLayer = def "fd547ab1-88e9-4fc2-9c7d-a67545bfd3d1" "Quadtree.VolumesBilinear4d.Layer" "Quadtree. VolumesBilinearParams4d layer. DurableMapAligned16." Durable.Primitives.DurableMapAligned16

let Mask1b = def "6f33738c-eddc-411e-932f-326249d95f46" "Aardvark.Geometry.Quadtree.ByteMask" "UInt8[]." Durable.Primitives.UInt8Array

let private def2layer = Map.ofList [
(Heights1f , Heights1fLayer )
(HeightsBilinear4f , HeightsBilinear4fLayer )
Expand Down
Loading

0 comments on commit 78cef35

Please sign in to comment.