diff --git a/src/Aardvark.Geometry.Quadtree/Builder.fs b/src/Aardvark.Geometry.Quadtree/Builder.fs index a17729e..1add0db 100644 --- a/src/Aardvark.Geometry.Quadtree/Builder.fs +++ b/src/Aardvark.Geometry.Quadtree/Builder.fs @@ -11,7 +11,7 @@ module Builder = let private debugOutput = #if DEBUG - true + false #else false #endif @@ -86,7 +86,7 @@ module Builder = else - let bbQuadrants = rootCell.Children |> Array.map (fun subCell -> subCell.GetBoundsForExponent(sampleExponent)) + //let bbQuadrants = rootCell.Children |> Array.map (fun subCell -> subCell.GetBoundsForExponent(sampleExponent)) let patchesPerQuadrant = rootCell.Children @@ -116,6 +116,106 @@ module Builder = let result = { Id = Guid.NewGuid(); ExactBoundingBox = ebb; Cell = rootCell; SplitLimitExponent = BuildConfig.Default.SplitLimitPowerOfTwo; HasMask = hasMask; SubNodes = subNodes } result |> InMemoryInner + let rec private build'' (minSampleExponent : int) (rootCell : Cell2d) (patches : LayerSet[]) = + + if debugOutput then + printfn "[DEBUG] build' rootCell = %A, %d patches" rootCell patches.Length + + for p in patches do + invariantm (p.SampleExponent >= minSampleExponent) + (fun () -> sprintf "Patch sample exponent %d is smaller than specified minimum sample exponent %d." p.SampleExponent minSampleExponent) + "28d1fdd1-2da6-4329-a065-c134c1351ffc" + + 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 minSampleExponent 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) + + // adjust root cell for split limit + let requiredRootCellSplitLimit = minSampleExponent + BuildConfig.Default.SplitLimitPowerOfTwo + + let mutable rootCell = rootCell + + if requiredRootCellSplitLimit <> rootCell.Exponent then + + invariantm (rootCell.Exponent < requiredRootCellSplitLimit) + (fun () -> sprintf "Expected root cell exponent %d to be smaller than requiredRootCellSplitLimit %d." rootCell.Exponent requiredRootCellSplitLimit) + "4911adf3-7b87-4234-9bcc-bc3076df846e" + + if debugOutput then + printfn "[DEBUG] must adjust root cell %A exponent to %d" rootCell requiredRootCellSplitLimit + + while rootCell.Exponent < requiredRootCellSplitLimit do rootCell <- rootCell.Parent + + if debugOutput then + printfn "[DEBUG] adjusted root cell is %A" rootCell + + let merged = LayerSet.Merge patches + let qnode = QNode(Guid.NewGuid(), ebb, rootCell, BuildConfig.Default.SplitLimitPowerOfTwo, merged) + + if debugOutput then + printfn "[DEBUG] CREATED QNode with split limit = %d" qnode.SplitLimitExponent + + //let xs = qnode |> InMemoryNode |> Query.All Query.Config.Default |> Seq.map (fun x -> x.GetSamples(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(minSampleExponent) + 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'' minSampleExponent subCell subPatches + ) + + let hasMask = subNodes |> Array.exists (fun n -> n.HasMask) + if debugOutput && hasMask then + printfn "[DEBUG] has mask %A" rootCell + let result = { Id = Guid.NewGuid(); ExactBoundingBox = ebb; Cell = rootCell; SplitLimitExponent = BuildConfig.Default.SplitLimitPowerOfTwo; HasMask = hasMask; SubNodes = subNodes } + result |> InMemoryInner + /// Creates a quadtree from many small patches. let Build (patches : LayerSet seq) : QNodeRef = let patches = patches |> Array.ofSeq @@ -188,6 +288,8 @@ type Builder () = /// Build a quadtree from all the patches that have been added to this builder. member this.Build () : QNodeRef option = + + let mutable mergesCount = 0 patches // (1) sort per-resolution patch lists from coarse to fine resolution ... @@ -198,7 +300,10 @@ type Builder () = |> Seq.fold (fun state item -> match state with | None -> Some item - | Some state -> Quadtree.Merge Dominance.SecondDominates state item |> Some + | Some state -> + mergesCount <- mergesCount + 1 + printfn "[Builder.Build()] mergesCount -> %d" mergesCount + Quadtree.Merge Dominance.SecondDominates state item |> Some ) None // initial state diff --git a/src/Aardvark.Geometry.Quadtree/Layer.fs b/src/Aardvark.Geometry.Quadtree/Layer.fs index c81b54b..5942062 100644 --- a/src/Aardvark.Geometry.Quadtree/Layer.fs +++ b/src/Aardvark.Geometry.Quadtree/Layer.fs @@ -5,6 +5,7 @@ open Aardvark.Data open System open System.Collections.Immutable open System.Collections.Generic +open System.Diagnostics #nowarn "44" @@ -134,20 +135,29 @@ type Layer<'a when 'a : equality>(def : Durable.Def, data : 'a[], mapping : Data | Fail -> if inside () then let i = mapping.GetBufferIndex s - data.[i] + match mask with + | None -> data[i] + | Some mask -> if mask[i] = 1uy then data[i] else failwith "Error 79671ba5-c509-4edd-ac4b-0f8d7b5e7222." + else failwith <| sprintf "Position %A outside of available data [%A-%A]." s this.SampleMin this.SampleMaxIncl + | ClampToBorder value -> if inside () then let i = mapping.GetBufferIndex s - data.[i] + match mask with + | None -> data[i] + | Some mask -> if mask[i] = 1uy then data[i] else failwith "Error 6befcffd-3f31-465b-b37a-43137339b6f8." else value + | ClampToEdge -> let x = if s.X < min.X then min.X elif s.X > maxIncl.X then maxIncl.X else s.X let y = if s.Y < min.Y then min.Y elif s.Y > maxIncl.Y then maxIncl.Y else s.Y let i = mapping.GetBufferIndex(x, y) - data.[i] + match mask with + | None -> data[i] + | Some mask -> if mask[i] = 1uy then data[i] else failwith "Error 472e5d8b-aa34-4ad9-a973-75d652a57f18." member this.GetSample (mode : BorderMode<'a>, globalPos : V2d) : 'a = let s = mapping.GetSampleCell globalPos @@ -300,8 +310,6 @@ module Layer = let i = finalMapping.GetBufferIndex c let v = layer.GetSample(Fail, c) - //finalData.[i] <- v - match finalMask[i] with | 255uy -> finalData[i] <- v @@ -342,6 +350,11 @@ module Layer = printfn "[DEBUG][Layer.mergeTyped] debugCountOccupied.Count = %d / %d ... %5.2f" coundOccupiedSamples finalMask.Length (float coundOccupiedSamples / float finalMask.Length) // rewrite mask (1 ... occupied, 0 ... undefined) + + let countOccupied = finalMask |> Array.filter (fun x -> x <> 255uy) |> Array.length + let countUndefined = finalMask |> Array.filter (fun x -> x = 255uy) |> Array.length + printfn "[OCCUPANCY][e = %d][%A][%A] countOccupied = %d, countUndefined = %d" e finalWindow finalWindow.Size countOccupied countUndefined + for i = 0 to finalMask.Length-1 do finalMask[i] <- if finalMask[i] = 255uy then 0uy else 1uy diff --git a/src/Aardvark.Geometry.Quadtree/Node.fs b/src/Aardvark.Geometry.Quadtree/Node.fs index 39e91dd..2b98a58 100644 --- a/src/Aardvark.Geometry.Quadtree/Node.fs +++ b/src/Aardvark.Geometry.Quadtree/Node.fs @@ -44,6 +44,9 @@ and QNode(uid : Guid, exactBoundingBox : Box2d, cell : Cell2d, splitLimitExp : i new (exactBoundingBox : Box2d, cell : Cell2d, splitLimitExp : int, layers : LayerSet) = QNode(Guid.NewGuid(), exactBoundingBox, cell, splitLimitExp, layers) + new (splitLimitExp : int, layers : LayerSet) = + QNode(Guid.NewGuid(), layers.BoundingBox, layers.BoundingBox |> Cell2d, splitLimitExp, layers) + member _.Id with get() = uid member _.Cell with get() = cell diff --git a/src/Aardvark.Geometry.Quadtree/Query.fs b/src/Aardvark.Geometry.Quadtree/Query.fs index 7cbbef5..f7028bd 100644 --- a/src/Aardvark.Geometry.Quadtree/Query.fs +++ b/src/Aardvark.Geometry.Quadtree/Query.fs @@ -4,7 +4,6 @@ open Aardvark.Base open Aardvark.Data open System open System.Collections.Generic -open System.Threading open System.Diagnostics module Query = @@ -77,7 +76,7 @@ module Query = let mergeDominatingT1 = Stopwatch() /// xs dominate always - let private mergeDominating (config : Config) (xs : Result list) (ys : Result list) (isSampleInside : Cell2d -> bool) : Result seq = + let private mergeDominatingPerSample (config : Config) (xs : Result list) (ys : Result list) (isSampleInside : Cell2d -> bool) : Result seq = if config.Verbose then printfn "[mergeDominating] xs:" @@ -93,7 +92,7 @@ module Query = let yebb = y.Node.ExactBoundingBox xs |> Array.filter(fun x -> x.Node.ExactBoundingBox.Intersects(yebb)) - seq { + let resultSeq = seq { mergeDominatingT0.Start(); @@ -115,11 +114,17 @@ module Query = let mutable result = List(8192) // all dominating sample cells intersecting dominated result - let zcs = zs |> Array.collect(fun z -> z.GetSampleCells() |> Array.filter(fun zc -> zc.BoundingBox.Intersects(yebb))) |> Array.map(fun c -> c.BoundingBox) + let zcs0 = zs |> Array.collect(fun z -> z.GetSampleCells()) + let zcs1 = zcs0 |> Seq.filter(fun zc -> zc.BoundingBox.Intersects(yebb)) |> Seq.toList + let zcs = zcs1 |> Seq.map(fun c -> c.BoundingBox) |> Seq.toList |> List.toArray // all sample cells of dominated result let ycs = y.GetSampleCells () + + let mutable i = 0 for yc in ycs do + i <- i + 1 + //printfn "[resolve dominate results] %d/%d" i ycs.Length let inline isDominatedCellYcFullyCovered (bb : Box2d) = zcs |> Array.exists(fun zc -> zc.Contains(bb)) let inline isDominatedCellYcInterferedWith (bb : Box2d) = @@ -170,15 +175,18 @@ module Query = mergeDominatingT0.Stop() } - let private merge (config : Config) (dom : Dominance) (xs : Result list) (ys : Result list) (isSampleInside : Cell2d -> bool) : Result list = + let result = resultSeq |> Seq.toArray + result + + let private mergePerSample (config : Config) (dom : Dominance) (xs : Result list) (ys : Result list) (isSampleInside : Cell2d -> bool) : Result list = match xs |> Seq.isEmpty, ys |> Seq.isEmpty with // both sequences contain values | false, false -> match dom with | FirstDominates - | MoreDetailedOrFirst -> mergeDominating config xs ys isSampleInside |> Seq.toList + | MoreDetailedOrFirst -> mergeDominatingPerSample config xs ys isSampleInside |> Seq.toList | SecondDominates - | MoreDetailedOrSecond -> mergeDominating config ys xs isSampleInside |> Seq.toList + | MoreDetailedOrSecond -> mergeDominatingPerSample config ys xs isSampleInside |> Seq.toList // xs contains values, ys is empty -> no need to merge, just return xs | false, true -> xs // xs is empty -> no need to merge, just return ys @@ -204,8 +212,8 @@ module Query = let xs = generic a |> Seq.toList let ys = generic b |> Seq.toList //printf "merge first (%d) + second (%d) %A ... " xs.Length ys.Length a.Cell - let r = merge config dom xs ys isSampleInside - let foo = r |> List.collect (fun x -> x.GetSampleCells() |> Array.toList) + let r = mergePerSample config dom xs ys isSampleInside + //let foo = r |> List.collect (fun x -> x.GetSampleCells() |> Array.toList) //printfn "done %d" (r |> List.sumBy (fun x -> x.GetSampleCells().Length)) r @@ -394,8 +402,8 @@ module Query = let result = { Node = n; Selection = FullySelected } if config.Verbose then printfn "[Generic ] fully inside (%A)" root.Cell - printfn "[Generic ] YIELD %A" result - for c in result.GetSampleCells() do printfn "[Generic ] Y %A" c + printfn "[Generic ] YIELD %A" result.Node.Cell + //for c in result.GetSampleCells() do printfn "[Generic ] Ya %A" c yield result else // partially inside @@ -405,8 +413,8 @@ module Query = if xs.Length > 0 then let result = { Node = n; Selection = CellsSelected xs } if config.Verbose then - printfn "[Generic ] YIELD %A" result - for c in result.GetSampleCells() do printfn "[Generic ] Y %A" c + printfn "[Generic ] YIELD %A" result.Node.Cell + //for c in result.GetSampleCells() do printfn "[Generic ] Yb %A" c yield result } diff --git a/src/Scratch/Program.fs b/src/Scratch/Program.fs index 64bdca9..12b6a54 100644 --- a/src/Scratch/Program.fs +++ b/src/Scratch/Program.fs @@ -1276,7 +1276,7 @@ let builderTest_20240112 () = let buildSerializationTest_20240202 () = - let path = Path.GetFullPath("c:/tmp/20240202_quadtreetest") + let path = Path.GetFullPath(@"c:\tmp\20240202_quadtreetest") printfn "path = %s" path @@ -1307,10 +1307,63 @@ let buildSerializationTest_20240202 () = () +let cp_20240202_quadtreetest () = + + let path = Path.GetFullPath(@"W:\Datasets\Vgm\Quadtree\20240202_quadtreetest") + + printfn "path = %s" path + + let options = SerializationOptions.NewInMemoryStore(verbose = false) + + let idFile = Guid(File.ReadAllText(@"W:\Datasets\Vgm\Quadtree\20240202_quadtreetest\builder.20240202122614.638424735748032405.key.txt")) + let builderReloadedFile = Builder.Import(path, idFile) + match builderReloadedFile with + | None -> printfn "reloaded from file = None" + | Some x -> + printfn "reloaded from file, %d patches" (x.GetPatches() |> Seq.length) + + let samplesCount = + x.GetPatches() + |> Seq.filter(fun patch -> patch.SampleExponent = -3) + |> Seq.sumBy(fun patch -> patch.SampleWindow.Area) + printfn("total samples count with e = -3: %d") samplesCount + + match x.Build () with + | None -> failwith "" + | Some qtree -> + let makeReturnValOfQueryResults (resultChunk : seq) (def : Aardvark.Data.Durable.Def) = + + let samples = + resultChunk + |> Seq.collect (fun chunk -> chunk.GetSamples def) + //|> Seq.map (fun (cell,bilParamV) -> let bilParam = BilinearSurfaceParams.FromV4f bilParamV + // (bilParam, cell)) + |> Seq.toList + + //posAndParams |> Seq.filter (fun pos -> (fst pos).b0.IsNaN() |> not) + // |> Seq.map (fun pos -> { originalCell = snd pos; surface = fst pos} ) + + //let mutable i = 0 + //for s in samples do + // i <- i + 1 + // printfn "%A" s + + samples + + let config = Query.Config.Default //{ Query.Config.Default with Verbose = true } + let resultCells = qtree |> Query.All config |> Seq.toArray + let samples = makeReturnValOfQueryResults resultCells Defs.HeightsBilinear4f + + () + + () + [] let main argv = - buildSerializationTest_20240202 () + cp_20240202_quadtreetest () + + //buildSerializationTest_20240202 () //builderTest_20240112 ()