diff --git a/src/Aardvark.Geometry.Quadtree.Tests/StructureTests.fs b/src/Aardvark.Geometry.Quadtree.Tests/StructureTests.fs index 9f803b0..ff66bd1 100644 --- a/src/Aardvark.Geometry.Quadtree.Tests/StructureTests.fs +++ b/src/Aardvark.Geometry.Quadtree.Tests/StructureTests.fs @@ -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) diff --git a/src/Aardvark.Geometry.Quadtree.sln b/src/Aardvark.Geometry.Quadtree.sln index f390d64..67b086f 100644 --- a/src/Aardvark.Geometry.Quadtree.sln +++ b/src/Aardvark.Geometry.Quadtree.sln @@ -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 @@ -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 diff --git a/src/Aardvark.Geometry.Quadtree/Aardvark.Geometry.Quadtree.fsproj b/src/Aardvark.Geometry.Quadtree/Aardvark.Geometry.Quadtree.fsproj index c9782d0..7887276 100644 --- a/src/Aardvark.Geometry.Quadtree/Aardvark.Geometry.Quadtree.fsproj +++ b/src/Aardvark.Geometry.Quadtree/Aardvark.Geometry.Quadtree.fsproj @@ -23,6 +23,7 @@ + diff --git a/src/Aardvark.Geometry.Quadtree/Builder.fs b/src/Aardvark.Geometry.Quadtree/Builder.fs new file mode 100644 index 0000000..e074649 --- /dev/null +++ b/src/Aardvark.Geometry.Quadtree/Builder.fs @@ -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(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>() + 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() + 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 + + \ No newline at end of file diff --git a/src/Aardvark.Geometry.Quadtree/Defs.fs b/src/Aardvark.Geometry.Quadtree/Defs.fs index 82ed9ec..ffcde3a 100644 --- a/src/Aardvark.Geometry.Quadtree/Defs.fs +++ b/src/Aardvark.Geometry.Quadtree/Defs.fs @@ -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 ) diff --git a/src/Aardvark.Geometry.Quadtree/Layer.fs b/src/Aardvark.Geometry.Quadtree/Layer.fs index 50e79a7..6a20875 100644 --- a/src/Aardvark.Geometry.Quadtree/Layer.fs +++ b/src/Aardvark.Geometry.Quadtree/Layer.fs @@ -14,10 +14,9 @@ type ILayer = abstract member WithWindow : Box2l -> ILayer option abstract member MapSingleCenteredSampleTo : Cell2d -> ILayer abstract member WithSemantic : Durable.Def -> ILayer - //abstract member ResampleUntyped : Cell2d -> ILayer - //abstract member SupersampleUntyped : unit -> ILayer abstract member Materialize : unit -> ILayer abstract member ToDurableMap : unit -> seq> + abstract member Mask : byte[] option [] module ILayerExtensions = @@ -45,55 +44,73 @@ module ILayerExtensions = let w = this.SampleWindow Box2l(w.Min * 2L, w.Max * 2L) -type Layer<'a>(def : Durable.Def, data : 'a[], mapping : DataMapping) = +type Layer<'a when 'a : equality>(def : Durable.Def, data : 'a[], mapping : DataMapping, mask : byte[] option) = do invariantm (data.Length >= mapping.WindowSize.X * mapping.WindowSize.Y) (fun()->sprintf "Mapping window %A exceeds available data (length=%d)." mapping.Window data.Length) "971a69a6-cd25-43b8-b85b-7dd783456552" + new(def : Durable.Def, data : 'a[], mapping : DataMapping) = Layer<'a>(def, data, mapping, None) + interface ILayer with + member this.Mask with get() = mask + member this.Def with get() = def member this.Mapping with get() = mapping member this.WithWindow (w : Box2l) = mapping.WithWindow(w) - |> Option.map (fun m -> Layer(def, data, m) :> ILayer) + |> Option.map (fun m -> Layer(def, data, m, mask) :> ILayer) member this.MapSingleCenteredSampleTo (c : Cell2d) = invariant (mapping.BufferOrigin.IsCenteredAtOrigin) "1a276e80-0404-4e69-9777-acbac1a4ed6a" invariant (c.TouchesOrigin) "e2326309-7171-4b9b-841e-4f0a5ef028d7" invariant (data.Length = 1) "bf2b4330-67fd-4283-880f-f9c96dafee61" - Layer(def, data, DataMapping(c, V2i.II)) :> ILayer + Layer(def, data, DataMapping(c, V2i.II), mask) :> ILayer member this.WithSemantic (newSemantic : Durable.Def) = - Layer(newSemantic, data, mapping) :> ILayer + Layer(newSemantic, data, mapping, mask) :> ILayer member this.Materialize () = if mapping.Window.Min = V2l.OO && mapping.WindowSize = mapping.BufferSize then this :> ILayer else let size = V2i(mapping.WindowSize) - let newdata = Array.zeroCreate<'a> (int mapping.Window.Area) + let newArrayLength = int mapping.Window.Area + let newdata = Array.zeroCreate<'a> newArrayLength let mutable i = 0 for y = 0 to size.Y - 1 do for x = 0 to size.X - 1 do let c = Cell2d(mapping.Window.Min.X + int64 x, mapping.Window.Min.Y + int64 y, mapping.BufferOrigin.Exponent) let s = this.GetSample(Fail, c) - newdata.[i] <- s + newdata[i] <- s i <- i + 1 + let newmask = + match mask with + | None -> None + | Some mask -> + let mutable i = 0 + let newmask = Array.zeroCreate newArrayLength + for y = 0 to size.Y - 1 do + for x = 0 to size.X - 1 do + newmask[i] <- mask[mapping.GetBufferIndex(x, y)] + i <- i + 1 + Some newmask + let m = DataMapping(Cell2d(mapping.Window.Min, mapping.BufferOrigin.Exponent), size) - Layer<'a>(def, newdata, m) :> ILayer + Layer<'a>(def, newdata, m, newmask) :> ILayer member this.ToDurableMap () = seq { - kvp Defs.Layer.DefId def.Id - kvp Defs.Layer.BufferOrigin mapping.BufferOrigin - kvp Defs.Layer.BufferSize mapping.BufferSize - kvp Defs.Layer.Window mapping.Window - kvp def data + yield kvp Defs.Layer.DefId def.Id + yield kvp Defs.Layer.BufferOrigin mapping.BufferOrigin + yield kvp Defs.Layer.BufferSize mapping.BufferSize + yield kvp Defs.Layer.Window mapping.Window + yield kvp def data + match mask with | Some x ->yield kvp Defs.Mask1b x | None -> () } member this.Def with get() = def @@ -110,9 +127,6 @@ type Layer<'a>(def : Durable.Def, data : 'a[], mapping : DataMapping) = if c.Exponent > min.Exponent then failwith "Invariant 4270bb56-29a0-4267-82d3-aacffd7c5e88." let mutable s = c while s.Exponent < min.Exponent do s <- s.Parent - //let s = if c.Exponent = min.Exponent then c - // elif c.Exponent + 1 = min.Exponent then c.Parent - // else failwith "Invariant 4d59a076-37ee-42d4-8326-2341be922a6c." let inline inside () = s.X >= min.X && s.Y >= min.Y && s.X <= maxIncl.X && s.Y <= maxIncl.Y @@ -144,37 +158,8 @@ module Layer = let private verbose = false type private M = ImmutableDictionary - let private foo = Map.ofList [ - (Defs.Heights1d, fun (map : M) -> map.[Defs.Heights1d]) - ] - - let Deserialize (buffer : byte[]) : ILayer = - let struct (def, o) = DurableCodec.Deserialize(buffer) - let map = o :?> ImmutableDictionary - let mapping = DataMapping( - map.[Defs.Layer.BufferOrigin] :?> Cell2d, - map.[Defs.Layer.BufferSize] :?> V2i, - map.[Defs.Layer.Window] :?> Box2l - ) - let data = map |> foo.[def] - match data with - | :? (int[]) -> Layer(def, data :?> int[], mapping) :> ILayer - | :? (int64[]) -> Layer(def, data :?> int64[], mapping) :> ILayer - | :? (float[]) -> Layer(def, data :?> float[], mapping) :> ILayer - | :? (float32[]) -> Layer(def, data :?> float32[], mapping) :> ILayer - | :? (V2f[]) -> Layer(def, data :?> V2f[], mapping) :> ILayer - | :? (V2d[]) -> Layer(def, data :?> V2d[], mapping) :> ILayer - | :? (V3f[]) -> Layer(def, data :?> V3f[], mapping) :> ILayer - | :? (V3d[]) -> Layer(def, data :?> V3d[], mapping) :> ILayer - | :? (V4f[]) -> Layer(def, data :?> V4f[], mapping) :> ILayer - | :? (V4d[]) -> Layer(def, data :?> V4d[], mapping) :> ILayer - | :? (C3b[]) -> Layer(def, data :?> C3b[], mapping) :> ILayer - | :? (C4b[]) -> Layer(def, data :?> C4b[], mapping) :> ILayer - | :? (C3f[]) -> Layer(def, data :?> C3f[], mapping) :> ILayer - | :? (C4f[]) -> Layer(def, data :?> C4f[], mapping) :> ILayer - | _ -> failwith <| sprintf "Unknown type %A. Invariant 4e797062-04a2-445f-9725-79f66823aff8." (data.GetType()) - let defineBuilder<'a> def = (def, fun mapping (data : obj) -> Layer<'a>(def, data :?> 'a[], mapping) :> ILayer) + let defineBuilder<'a when 'a : equality> def = (def, fun mapping (data : obj) mask -> Layer<'a>(def, data :?> 'a[], mapping, mask) :> ILayer) let private builders = Map.ofList [ defineBuilder Defs.Heights1f defineBuilder Defs.Heights1d @@ -214,10 +199,55 @@ module Layer = map.[Defs.Layer.Window] :?> Box2l ) + let mask = + match map.TryGetValue(Defs.Mask1b) with + | (false, _) -> None + | (true, :? (byte[])) as (_, x) -> Some (x :?> byte[]) + | _ -> failwith "Internal error 30ae3cd8-c06a-4698-9db3-a6ef69706e6a." + match builders |> Map.tryFind def with - | Some builder -> builder mapping (map.[def]) + | Some builder -> builder mapping (map.[def]) mask | None -> sprintf "Unknown layer type %A. 8c90faa2-de10-4938-b8ee-3034bd9bdea0." def |> failwith + let Deserialize (buffer : byte[]) : ILayer = + + let foo = Map.ofList [ + (Defs.Heights1d, fun (map : M) -> map.[Defs.Heights1d]) + ] + + let struct (def, o) = DurableCodec.Deserialize(buffer) + let map = o :?> ImmutableDictionary + let mapping = DataMapping( + map.[Defs.Layer.BufferOrigin] :?> Cell2d, + map.[Defs.Layer.BufferSize] :?> V2i, + map.[Defs.Layer.Window] :?> Box2l + ) + + let mask = + match map.TryGetValue(Defs.Mask1b) with + | (false, _) -> None + | (true, :? (byte[])) as (_, x) -> Some (x :?> byte[]) + | _ -> failwith "Internal error 0be664f2-2bb5-4023-8821-7bfd371d1ac6." + + let data = map |> foo.[def] + match data with + | :? (int[]) -> Layer (def, data :?> int[] , mapping, mask) :> ILayer + | :? (int64[]) -> Layer (def, data :?> int64[] , mapping, mask) :> ILayer + | :? (float[]) -> Layer (def, data :?> float[] , mapping, mask) :> ILayer + | :? (float32[]) -> Layer(def, data :?> float32[], mapping, mask) :> ILayer + | :? (V2f[]) -> Layer (def, data :?> V2f[] , mapping, mask) :> ILayer + | :? (V2d[]) -> Layer (def, data :?> V2d[] , mapping, mask) :> ILayer + | :? (V3f[]) -> Layer (def, data :?> V3f[] , mapping, mask) :> ILayer + | :? (V3d[]) -> Layer (def, data :?> V3d[] , mapping, mask) :> ILayer + | :? (V4f[]) -> Layer (def, data :?> V4f[] , mapping, mask) :> ILayer + | :? (V4d[]) -> Layer (def, data :?> V4d[] , mapping, mask) :> ILayer + | :? (C3b[]) -> Layer (def, data :?> C3b[] , mapping, mask) :> ILayer + | :? (C4b[]) -> Layer (def, data :?> C4b[] , mapping, mask) :> ILayer + | :? (C3f[]) -> Layer (def, data :?> C3f[] , mapping, mask) :> ILayer + | :? (C4f[]) -> Layer (def, data :?> C4f[] , mapping, mask) :> ILayer + | _ -> failwith <| sprintf "Unknown type %A. Invariant 4e797062-04a2-445f-9725-79f66823aff8." (data.GetType()) + + let BoundingBox (layer : ILayer) = layer.Mapping.BoundingBox let Window (layer : ILayer) = layer.Mapping.Window @@ -231,7 +261,7 @@ module Layer = invariant (layers |> Array.forall (fun l -> l.Def = def)) "49514f12-b4f9-4708-9108-94a6dcc5d217" def - let private mergeTyped (layers : Layer<'a>[]) : Layer<'a> = + let private mergeTyped<'a when 'a : equality> (undefinedValue : 'a) (layers : Layer<'a>[]) : Layer<'a> = let def = ensureSameDef layers if verbose then printfn "[Layer.Merge] .... def = %s" def.Name @@ -246,7 +276,8 @@ module Layer = let finalOrigin = Cell2d(finalWindow.Min, e) let finalMapping = DataMapping(finalOrigin, V2i finalWindow.Size, finalWindow) let finalData = Array.zeroCreate<'a> (int finalWindow.Size.X * int finalWindow.Size.Y) - + let finalMask = Array.create (int finalWindow.Size.X * int finalWindow.Size.Y) 255uy + if verbose then for l in layers do printfn "[Layer.Merge] .... [%A-%A]" l.SampleMin l.SampleMaxIncl printfn "[Layer.Merge] .... final mapping" @@ -254,7 +285,11 @@ module Layer = printfn "[Layer.Merge] .... buffer size : %A" finalWindow.Size printfn "[Layer.Merge] .... window: %A" finalWindow - + let mutable debugCountValues = 0 + let mutable debugCountCollisions = 0 + let debugCollisionSamples = HashSet() + + let mutable layerIndex = 0uy for layer in layers do let w = layer.Mapping.Window let xMaxIncl = int w.SizeX - 1 @@ -264,17 +299,62 @@ module Layer = let c = Cell2d(w.Min.X + int64 x, w.Min.Y + int64 y, e) let i = finalMapping.GetBufferIndex c let v = layer.GetSample(Fail, c) - finalData.[i] <- v + + //finalData.[i] <- v + + match finalMask[i] with + | 255uy -> + finalData[i] <- v + finalMask[i] <- layerIndex + debugCountValues <- debugCountValues + 1 + | _ -> + if v <> finalData[i] then + if finalData[i] = undefinedValue then + if v <> undefinedValue then + finalData[i] <- v + finalMask[i] <- layerIndex + else + () + else + if v <> undefinedValue then + debugCollisionSamples.Add(i) |> ignore + debugCountCollisions <- debugCountCollisions + 1 + + if verbose then + printfn "[DEBUG][Layer.mergeTyped] COLLISION overwriting value %A from layer %d with value %A from layer %d" finalData[i] finalMask[i] v layerIndex + + else + () + + + + + layerIndex <- layerIndex + 1uy + - Layer(def, finalData, finalMapping) + // count occupied samples + let coundOccupiedSamples = finalMask |> Array.sumBy (fun x -> if x = 255uy then 1 else 0) - let private toTyped<'a> (layers : ILayer[]) : Layer<'a>[] = + if verbose && debugCountCollisions > 0 then + printfn "[DEBUG][Layer.mergeTyped] debugCountValues = %d" debugCountValues + printfn "[DEBUG][Layer.mergeTyped] debugCountCollisions = %d" debugCountCollisions + printfn "[DEBUG][Layer.mergeTyped] debugCollisionSamples.Count = %d" debugCollisionSamples.Count + printfn "[DEBUG][Layer.mergeTyped] debugCountOccupied.Count = %d / %d ... %5.2f" coundOccupiedSamples finalMask.Length (float coundOccupiedSamples / float finalMask.Length) + + // rewrite mask (1 ... occupied, 0 ... undefined) + for i = 0 to finalMask.Length-1 do + finalMask[i] <- if finalMask[i] = 255uy then 0uy else 1uy + + Layer(def, finalData, finalMapping, if coundOccupiedSamples > 0 then Some finalMask else None) + + let private toTyped<'a when 'a : equality> (layers : ILayer[]) : Layer<'a>[] = layers |> Array.map (fun x -> x :?> Layer<'a>) - let private mergeUntyped_<'a> xs = xs |> toTyped<'a> |> mergeTyped :> ILayer + let private mergeUntyped_<'a when 'a : equality> (undefinedValue : 'a) xs : ILayer = + xs |> toTyped<'a> |> (mergeTyped undefinedValue) :> ILayer /// Merge layers of same type (def). - let Merge (layers : ILayer seq) : ILayer option = + let Merge (layers : ILayer seq) = let ls = layers |> Array.ofSeq match ls.Length with | 0 -> @@ -282,43 +362,49 @@ module Layer = None | 1 -> if verbose then printfn "[Layer.Merge] 1 layer -> Some ls.[0]" - Some ls.[0] + Some (ls |> Array.exactlyOne) | n -> let distinctDefCount = ls |> Seq.distinctBy (fun l -> l.Def) |> Seq.length if distinctDefCount <> 1 then failwith "Can only merge layers of same type (def). Error 68bf8529-e9c5-4b7b-a100-3e4850fc9c33." if verbose then printfn "[Layer.Merge] %d layers" n - match ls.[0] with - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | :? Layer -> ls |> mergeUntyped_ - | _ -> failwith <| sprintf "Unsupported layer type %A. Invariant bfb8d2ec-666d-4878-b612-f46f59dd5e82." ls.[0] - - |> Some - + let mergedLayers = + match ls.[0] with + | :? Layer -> ls |> mergeUntyped_ Int32.MinValue + | :? Layer -> ls |> mergeUntyped_ Int64.MinValue + | :? Layer -> ls |> mergeUntyped_ nan + | :? Layer -> ls |> mergeUntyped_ nanf + | :? Layer -> ls |> mergeUntyped_ V2f.NaN + | :? Layer -> ls |> mergeUntyped_ V2d.NaN + | :? Layer -> ls |> mergeUntyped_ V3f.NaN + | :? Layer -> ls |> mergeUntyped_ V3d.NaN + | :? Layer -> ls |> mergeUntyped_ V4f.NaN + | :? Layer -> ls |> mergeUntyped_ V4d.NaN + | :? Layer -> ls |> mergeUntyped_ (C3b(0, 0, 0)) + | :? Layer -> ls |> mergeUntyped_ (C4b(0, 0, 0, 0)) + | :? Layer -> ls |> mergeUntyped_ (C3f(0, 0, 0)) + | :? Layer -> ls |> mergeUntyped_ (C4f(0, 0, 0, 0)) + | _ -> failwith <| sprintf "Unsupported layer type %A. Invariant bfb8d2ec-666d-4878-b612-f46f59dd5e82." ls.[0] + + Some mergedLayers type LayerSet(layers : ILayer[]) = do invariantm (layers.Length > 0) (fun()->"No layers.") "3532f669-8321-4f47-a39d-8c9920cb5f67" + + let sampleExponent = layers |> Seq.map (fun x -> x.SampleExponent) |> Seq.distinct |> Seq.tryExactlyOne + invariantm (sampleExponent.IsSome) (fun()->"Layers have different sample exponents.") "975ddfbd-ae0d-4b54-bad6-cd054faeadf9" + + () - member this.Layers with get() = layers + member this.Layers with get() = layers - member this.BoundingBox with get() = layers.[0].BoundingBox - member this.Mapping with get() = layers.[0].Mapping - member this.SampleExponent with get() = layers.[0].Mapping.BufferOrigin.Exponent - member this.SampleWindow with get() = layers.[0].Mapping.Window + member this.BoundingBox with get() = layers.[0].BoundingBox + member this.Mapping with get() = layers.[0].Mapping + member this.SampleExponent with get() = layers.[0].Mapping.BufferOrigin.Exponent + member this.SampleWindow with get() = layers.[0].Mapping.Window + member this.SampleWindowAtChildLevel with get() = layers.[0].SampleWindowAtChildLevel /// Returns 2.0 ^ SampleExponent. member this.SampleSize with get() = 2.0 ** float(layers.[0].Mapping.BufferOrigin.Exponent) @@ -326,7 +412,7 @@ type LayerSet(layers : ILayer[]) = member this.TryGetLayer (semantic : Durable.Def) : ILayer option = layers |> Array.tryFind (fun x -> x.Def.Id = semantic.Id) - member this.TryGetLayer<'a> (semantic : Durable.Def) : Layer<'a> option = + member this.TryGetLayer<'a when 'a : equality> (semantic : Durable.Def) : Layer<'a> option = this.TryGetLayer(semantic) |> Option.map (fun x -> x :?> Layer<'a>) member this.GetLayer(semantic : Durable.Def) : ILayer = @@ -335,7 +421,7 @@ type LayerSet(layers : ILayer[]) = with | _ -> sprintf "Failed to retrieve layer %A. Available layers are %A. Error 091474a1-d06b-408a-8609-85e0b272e645." semantic.Name (layers |> Array.map(fun x->x.Def.Name)) |> failwith - member this.GetLayer<'a>(semantic : Durable.Def) : Layer<'a> = + member this.GetLayer<'a when 'a : equality>(semantic : Durable.Def) : Layer<'a> = this.GetLayer(semantic) :?> Layer<'a> member this.WithWindow (w : Box2l) : LayerSet option = @@ -369,4 +455,29 @@ type LayerSet(layers : ILayer[]) = let l = oldLayer.WithSemantic(newSemantic) layers |> Seq.filter (fun x -> x.Def.Id <> oldSemantic.Id) |> Seq.append [l] |> Seq.toArray - LayerSet(newLayers) |> Some \ No newline at end of file + LayerSet(newLayers) |> Some + + /// True, if at least one layer has a mask. + member this.HasMask with get () = this.Layers |> Array.exists (fun x -> x.Mask.IsSome) + +module LayerSet = + + /// Merges multiple layer sets. + /// All layer sets must have identical layers (same number, same semantics, same order). + let Merge (layerSets : LayerSet seq) : LayerSet = + + let layerSets = layerSets |> Array.ofSeq + let layersPerLayerSet = layerSets[0].Layers.Length + + let indices = [0..layersPerLayerSet-1] + + let layers = + indices + |> List.choose (fun i -> + // merge the i-th layers from all layer sets + layerSets |> Array.map (fun x -> x.Layers[i]) |> Layer.Merge + ) + |> Array.ofList + + let result = LayerSet layers + result \ No newline at end of file diff --git a/src/Aardvark.Geometry.Quadtree/Merge.fs b/src/Aardvark.Geometry.Quadtree/Merge.fs index 757e1d6..581f1f6 100644 --- a/src/Aardvark.Geometry.Quadtree/Merge.fs +++ b/src/Aardvark.Geometry.Quadtree/Merge.fs @@ -78,10 +78,22 @@ with module Merge = let winner dom (first : QNodeRef) (second : QNodeRef) : QNodeRef option = - match dom, first.ExactBoundingBox, second.ExactBoundingBox with - | FirstDominates, bb1, bb2 when bb1.Contains(bb2) -> Some first - | SecondDominates, bb1, bb2 when bb2.Contains(bb1) -> Some second - | _ -> None + + None + + (* + removed optimization, because this no longer works when masks are involved + + - adding mask info (e.g. HasMask for all nodes/subtrees) would break backwards compatibility in serialization + - we can add this back again if it turns out that this makes a huge difference + (and would then also need to add more metadata and upgrade serialization accordingly) + *) + + //match dom, first.ExactBoundingBox, second.ExactBoundingBox with + // | FirstDominates, bb1, bb2 when bb1.Contains(bb2) -> Some first + // | SecondDominates, bb1, bb2 when bb2.Contains(bb1) -> Some second + // | _ -> None + let growParent (n : QNodeRef) : QNodeRef = QInnerNode.ofSubNode(n) |> InMemoryInner diff --git a/src/Aardvark.Geometry.Quadtree/Node.fs b/src/Aardvark.Geometry.Quadtree/Node.fs index 7997992..fb44b7e 100644 --- a/src/Aardvark.Geometry.Quadtree/Node.fs +++ b/src/Aardvark.Geometry.Quadtree/Node.fs @@ -70,13 +70,13 @@ and QNode(uid : Guid, exactBoundingBox : Box2d, cell : Cell2d, splitLimitExp : i member this.TryGetLayer (semantic : Durable.Def) : ILayer option = layers.TryGetLayer(semantic) - member this.TryGetLayer<'a> (semantic : Durable.Def) : Layer<'a> option = + member this.TryGetLayer<'a when 'a : equality> (semantic : Durable.Def) : Layer<'a> option = layers.TryGetLayer<'a>(semantic) member this.GetLayer(semantic : Durable.Def) : ILayer = layers.GetLayer(semantic) - member this.GetLayer<'a>(semantic : Durable.Def) : Layer<'a> = + member this.GetLayer<'a when 'a : equality>(semantic : Durable.Def) : Layer<'a> = layers.GetLayer<'a>(semantic) member this.WithWindow (w : Box2l) : QNode option = @@ -103,6 +103,9 @@ and QNode(uid : Guid, exactBoundingBox : Box2d, cell : Cell2d, splitLimitExp : i member this.GetSample (p : V2d) : Cell2d = layers.Mapping.GetSampleCell(p) + /// Resolution level. + member this.SampleExponent with get() = layers.SampleExponent + /// Returns 2.0 ^ SampleExponent. member this.SampleSize with get() : float = layers.SampleSize @@ -138,7 +141,7 @@ and | OutOfCoreNode of Guid * (unit -> QNodeRef) | InMemoryInner of QInnerNode | InMemoryMerge of QMergeNode - | LinkedNode of QLinkedNode + | LinkedNode of QLinkedNode with /// Forces property Id. Throws exception if NoNode. @@ -270,7 +273,7 @@ and | LinkedNode n -> n.Target.GetLayer def /// Throws if no such layer. - member this.GetLayer<'a> def = + member this.GetLayer<'a when 'a : equality> def = match this with | NoNode | InMemoryInner _ @@ -290,7 +293,7 @@ and | None -> None | LinkedNode n -> n.Target.TryGetLayer def - member this.TryGetLayer<'a> def = + member this.TryGetLayer<'a when 'a : equality> def = match this with | NoNode | InMemoryInner _ diff --git a/src/Aardvark.Geometry.Quadtree/Prelude.fs b/src/Aardvark.Geometry.Quadtree/Prelude.fs index 30bd544..5790672 100644 --- a/src/Aardvark.Geometry.Quadtree/Prelude.fs +++ b/src/Aardvark.Geometry.Quadtree/Prelude.fs @@ -34,7 +34,18 @@ module Prelude = let inline inDifferentQuadrants (a : Cell2d) (b : Cell2d) = if a.IsCenteredAtOrigin || b.IsCenteredAtOrigin then false else ((a.X >= 0L) <> (b.X >= 0L)) || ((a.Y >= 0L) <> (b.Y >= 0L)) - + + /// Gets cell bounds in resolution 2^e with inclusive min and exclusive max. + let getBoundsForExponent (e : int) (cell : Cell2d) : Box2l = + if cell.IsCenteredAtOrigin then + invariant (e < cell.Exponent) "ecfc0260-688b-43f2-9e10-a0bc66132ac1" + let d = cell.Exponent - e - 1 + let x = 1L <<< d + Box2l(-x, -x, x, x) + else + invariant (e <= cell.Exponent) "316764bd-c4fe-4202-bba4-bf603061b629" + let d = cell.Exponent - e + Box2l(cell.X <<< d, cell.Y <<< d, (cell.X + 1L) <<< d, (cell.Y + 1L) <<< d) module Option = diff --git a/src/Aardvark.Geometry.Quadtree/PrettyPrint.fs b/src/Aardvark.Geometry.Quadtree/PrettyPrint.fs index 4372d56..c9be2b6 100644 --- a/src/Aardvark.Geometry.Quadtree/PrettyPrint.fs +++ b/src/Aardvark.Geometry.Quadtree/PrettyPrint.fs @@ -129,7 +129,7 @@ module PrettyPrint = | -3 -> C3b(128,255,255) | _ -> C3b.Gray80 - let ofQNodeRef<'a> (name : string) (pos : Pos) (def : Durable.Def) (qref : QNodeRef) : Cells = + let ofQNodeRef<'a when 'a : equality> (name : string) (pos : Pos) (def : Durable.Def) (qref : QNodeRef) : Cells = let f = { HAlign=Left; VAlign=Top; Bgcolor=C3b.White } @@ -203,11 +203,11 @@ module PrettyPrint = - let generateHtmlDebugView<'a> title def quadtrees = + let generateHtmlDebugView<'a when 'a : equality> title def quadtrees = let content = quadtrees |> List.mapi (fun i x -> Cells.ofQNodeRef<'a> (sprintf "

%s

" (fst x)) {X=0;Y=i*2} def (snd x)) Cells.Group({X=0;Y=0}, Format.Default, sprintf "

%s

" title, content) |> Cells.toHtml - let showHtmlDebugView<'a> title def quadtrees = + let showHtmlDebugView<'a when 'a : equality> title def quadtrees = let html = generateHtmlDebugView<'a> title def quadtrees let now = DateTime.Now let filename = sprintf "quadtree_%04d-%02d-%02d-%02d-%02d-%02d.html" now.Year now.Month now.Day now.Hour now.Minute now.Second diff --git a/src/Aardvark.Geometry.Quadtree/Quadtree.fs b/src/Aardvark.Geometry.Quadtree/Quadtree.fs index fbc4d7a..bd52f03 100644 --- a/src/Aardvark.Geometry.Quadtree/Quadtree.fs +++ b/src/Aardvark.Geometry.Quadtree/Quadtree.fs @@ -28,6 +28,50 @@ module Quadtree = | OutOfCoreNode (_, load) -> load() | _ -> node + /// Enumerate all nodes. + /// Out-of-core nodes are automatically loaded and traversed. + /// A resulting QNode object x can be simply converted back to a QNodeRef by calling `InMemoryNode x`. + let EnumerateLeafNodesInMemory (root : QNodeRef) : seq = seq { + + let stack = Stack() + stack.Push root + + while stack.Count > 0 do + + let node = stack.Pop() |> EnsureInMemory + + match node with + | NoNode -> () + | InMemoryNode q -> yield q + | OutOfCoreNode _ -> failwith "Internal error 7a6995bd-c0d3-4650-8ea5-9764ec3fe26c." + | InMemoryInner n -> for subNode in n.SubNodes do subNode |> stack.Push + | InMemoryMerge n -> n.First |> stack.Push + n.Second |> stack.Push + | LinkedNode n -> n.Target |> stack.Push + } + + /// Enumerate all nodes of given quadtree. + /// If outOfCore is true, then the full tree is traversed, even nodes that are stored out-of-core. + let EnumerateNodes (outOfCore : bool) (root : QNodeRef) : seq = seq { + + let stack = Stack() + stack.Push root + + while stack.Count > 0 do + + let node = stack.Pop() + + match node with + | NoNode -> () + | InMemoryNode n -> yield node + | OutOfCoreNode (_, load) -> if outOfCore then stack.Push (load()) else yield node + | InMemoryInner n -> for subNode in n.SubNodes do subNode |> stack.Push + | InMemoryMerge n -> n.First |> stack.Push + n.Second |> stack.Push + yield node + | LinkedNode n -> n.Target |> stack.Push + } + /// Enumerate all nodes breadth first. /// If outOfCore is false, then out-of-core nodes are handled like leaf nodes. let EnumerateNodesBreadthFirst (outOfCore : bool) (root : QNodeRef) : seq = seq { @@ -86,16 +130,26 @@ module Quadtree = count /// Count number of nodes in quadtree. - /// If outOfCore is false, then out-of-core nodes are handled like leafs. + /// If outOfCore is false, then out-of-core nodes are handled like leaf nodes. let CountNodes outOfCore root = root |> tryCount outOfCore 1 1 /// Count number of leaf nodes in quadtree. - /// If outOfCore is false, then out-of-core nodes are handled like leafs. - let CountLeafs outOfCore root = root |> tryCount outOfCore 1 0 + /// If outOfCore is false, then out-of-core nodes are handled like leaf nodes. + let CountLeafNodes outOfCore root = root |> tryCount outOfCore 1 0 + + [] + let CountLeafs = CountLeafNodes /// Count number of inner nodes in quadtree. - /// If outOfCore is false, then out-of-core nodes are handled like leafs. - let CountInner outOfCore root = root |> tryCount outOfCore 0 1 + /// If outOfCore is false, then out-of-core nodes are handled like leaf nodes. + let CountInnerNodes outOfCore root = root |> tryCount outOfCore 0 1 + + [] + let CountInner = CountInnerNodes + + /// Count number of merge nodes in quadtree. + /// If outOfCore is false, then out-of-core nodes are handled like leaf nodes. + let CountMergeNodes outOfCore root : int = root |> EnumerateNodes outOfCore |> Seq.filter (fun n -> match n with | InMemoryMerge _ -> true | _ -> false) |> Seq.length let PrintStructure (outOfCore : bool) (n : QNodeRef) = @@ -153,7 +207,7 @@ module Quadtree = for i = 0 to 3 do match subNodes.[i] with | InMemoryNode x -> invariant (x.Cell = children.[i]) "15f2c6c3-6f5b-4ac0-9ec0-8ab968ac9c2e" - | InMemoryInner x -> invariant (x.Cell = children.[i]) "817d1b42-8fd9-465c-9cac-4d197a9a5bb9" + | InMemoryInner x -> invariant (x.Cell = children.[i]) "817d1b42-8fd9-465c-9cac-4d197a9a5bb9" | NoNode -> () | _ -> failwith "Todo 6f5d63bc-ece6-49ef-b634-879f81a4fc36." @@ -211,7 +265,7 @@ module Quadtree = qtree.ContainsLayer(semantic) /// Throws if no such layer. - let GetLayer<'a> (semantic : Durable.Def) (qtree : QNodeRef) : Layer<'a> = + let GetLayer<'a when 'a : equality> (semantic : Durable.Def) (qtree : QNodeRef) : Layer<'a> = qtree.GetLayer<'a>(semantic) /// Throws if no such layer. @@ -219,7 +273,7 @@ module Quadtree = qtree.GetLayer(semantic) /// Throws if no such layer. - let TryGetLayer<'a> (semantic : Durable.Def) (qtree : QNodeRef) : Layer<'a> option = + let TryGetLayer<'a when 'a : equality> (semantic : Durable.Def) (qtree : QNodeRef) : Layer<'a> option = qtree.TryGetLayer<'a>(semantic) /// Throws if no such layer. diff --git a/src/Aardvark.Geometry.Quadtree/Query.fs b/src/Aardvark.Geometry.Quadtree/Query.fs index effbbaa..dc97c17 100644 --- a/src/Aardvark.Geometry.Quadtree/Query.fs +++ b/src/Aardvark.Geometry.Quadtree/Query.fs @@ -62,7 +62,7 @@ module Query = | SubtractionSelected second -> this.Node.LayerSet.SampleWindow.GetAllSamplesFromFirstMinusSecond(second, this.Node.LayerSet.SampleExponent) | FullySelected -> this.Node.GetAllSamples() - member this.GetSamples<'a>(def : Durable.Def) : (Cell2d*'a)[] = + member this.GetSamples<'a when 'a : equality>(def : Durable.Def) : (Cell2d*'a)[] = let layer = this.Node.GetLayer<'a>(def) let cells = this.GetSampleCells () let result = cells |> Array.map (fun p -> @@ -628,7 +628,7 @@ module Sample = } with member this.GetSampleCells () : Cell2d[] = this.Cells - member this.GetSamples<'a>(def : Durable.Def) : (V2d*Cell2d*'a)[] = + member this.GetSamples<'a when 'a : equality>(def : Durable.Def) : (V2d*Cell2d*'a)[] = let layer = this.Node.GetLayer<'a>(def) this.Cells |> Array.map2 (fun p c -> (p, c, layer.GetSample(Fail, c))) this.Positions @@ -747,7 +747,7 @@ module Sample = let result = Positions config [| position |] root |> Seq.toArray result |> Seq.tryHead - let PositionTyped<'a> (config : Query.Config) (position : V2d) (def : Durable.Def) (root : QNodeRef) : 'a option = + let PositionTyped<'a when 'a : equality> (config : Query.Config) (position : V2d) (def : Durable.Def) (root : QNodeRef) : 'a option = match Position config position root with | Some x -> let xs = x.GetSamples<'a> def diff --git a/src/Scratch/Program.fs b/src/Scratch/Program.fs index c73070c..c9a5994 100644 --- a/src/Scratch/Program.fs +++ b/src/Scratch/Program.fs @@ -1010,45 +1010,111 @@ let madorjan20211103 () = let flattenSketch () = - let store = @"W:\Datasets\Vgm\Data\2023-09-04_bugreport" - let key = Guid "c0d343c1-8aaf-4884-9f98-4627b4f09396" + let store = @"W:\Datasets\Vgm\Data\2023-09-04_quadtree" + //let store = @"W:\Datasets\Vgm\Data\2023-11-10_quadtree" + + let key = File.ReadAllText(Path.Combine(store, "key.txt")) |> Guid let options = Serialization.SerializationOptions.SimpleDiskStore store + let root : QNodeRef = Quadtree.Load options key - let root = Quadtree.Load options key + let leafNodes = root |> Quadtree.EnumerateLeafNodesInMemory |> List.ofSeq + printfn "original quadtree has %d leaf nodes and %d merge nodes" leafNodes.Length (Quadtree.CountMergeNodes true root) + - //let nodeCount = root |> Quadtree.enumerateNodesBreadthFirst true |> Seq.length - printfn "node count (all) = %d" (root |> Quadtree.CountNodes true) - printfn "node count (inner) = %d" (root |> Quadtree.CountInner true) - printfn "node count (leafs) = %d" (root |> Quadtree.CountLeafs true) + let resolutions = leafNodes |> List.groupBy (fun n -> n.SampleExponent) - let foo (root : QNodeRef) : seq = seq { - - let ensureInMemory n = match n with | OutOfCoreNode (_, load) -> load() | _ -> n - let stack = Stack() - stack.Push root + let builder = Builder() + for n in leafNodes do builder.Add n + + match builder.Build() with + | None -> printfn "" + | Some newAndBetterTree -> + let countLeafNodes = newAndBetterTree |> Quadtree.CountLeafNodes true + let countMergeNodes = newAndBetterTree |> Quadtree.CountMergeNodes true + //for n in Quadtree.EnumerateNodes true newAndBetterTree do printfn "%A" (n.GetType()) + printfn "new quadtree has %d leaf nodes and %d merge nodes" countLeafNodes countMergeNodes - while stack.Count > 0 do - let node = stack.Pop() |> ensureInMemory + + - match node with - | NoNode -> () - | InMemoryNode _ -> yield node - | OutOfCoreNode _ -> failwith "Internal error 7a6995bd-c0d3-4650-8ea5-9764ec3fe26c." - | InMemoryInner n -> yield node - for subNode in n.SubNodes do subNode |> stack.Push - | InMemoryMerge n -> yield node - n.First |> stack.Push - n.Second |> stack.Push - | LinkedNode n -> n.Target |> stack.Push - } + ////let nodeCount = root |> Quadtree.enumerateNodesBreadthFirst true |> Seq.length + //printfn "node count (all) = %d" (root |> Quadtree.CountNodes true) + //printfn "node count (inner) = %d" (root |> Quadtree.CountInner true) + //printfn "node count (leafs) = %d" (root |> Quadtree.CountLeafs true) - let fooSeq = foo root |> List.ofSeq - let nodeCount = root |> foo |> Seq.length - printfn "node count (foo ) = %d" nodeCount + //printfn "" + //printfn "grouping by sample size" + //let gs = leafNodes |> Seq.groupBy (fun n -> n.LayerSet.SampleExponent) |> List.ofSeq + //let histo = gs + // |> Seq.map (fun (key, ns) -> (key, ns |> List.ofSeq)) + // //|> Seq.sortByDescending (fun (key, _) -> key) + + //for (k, ns) in histo do + + // let countPatches = ns |> List.length + // let countSamples = ns |> List.sumBy (fun x -> x.LayerSet.SampleWindow.Area) + // let ebb = ns |> Seq.map (fun n -> n.ExactBoundingBox) |> Box2d + // printfn " %d" k + // printfn " patches %8d" countPatches + // printfn " samples %8d" countSamples + // printfn " ebb %A" ebb + // printfn "" + + // //let result = ns |> List.map (fun n -> n.LayerSet) |> Builder.Build + + // for n in ns do builder.Add(n.LayerSet) + + // () + + //let boxes = List() + //for n in ns do + // let foo = n |> InMemoryNode |> Query.All Query.Config.Default |> Seq.collect (fun x -> x.GetSampleCells ()) |> Seq.groupBy id |> Seq.filter (fun (_, xs) -> xs |> Seq.length <> 1) |> Array.ofSeq + // for (c, cs) in foo do printfn "%A -> %d" c (cs |> Seq.length) + // // printfn "[%d] %A" k n.LayerSet.SampleWindow + // boxes.Add(n.LayerSet.SampleWindow) + // () + + //let mergeBoxes (boxes : List) : List = + // let gbb = Box2l(boxes) + // printfn "gbb %A with size = %A" gbb gbb.Size + + // let candidateSets = boxes |> Seq.groupBy (fun box -> box.Min.X) |> Seq.map (fun (k, bs) -> (k, bs |> List.ofSeq)) |> List.ofSeq + // printfn " candidate sets : %d" candidateSets.Length + // printfn " candidate sets (>1): %d" (candidateSets |> Seq.filter (fun (k, bs) -> bs.Length > 1) |> Seq.length) + + // //for (minX, bs) in candidateSets do + // // let groupedByWidth = bs |> Seq.groupBy (fun b -> b.Size.X) |> List.ofSeq + // // for (w, bs) in groupedByWidth do + // // // same min.X, same width + // // printfn "minX = %d, width = %d, count = %d" minX w (bs |> Seq.length) + // // let bs2 = bs |> Seq.sortBy (fun b -> b.Min.Y) + // // for b in bs2 do + // // printfn " * %A %A" b b.Size + + + // let result = List() + // result + + //let boxesMerged = mergeBoxes boxes + + //printfn " merged boxes ... %d" boxesMerged.Count + + + //printfn "" + //let nodeCount = root |> enumerateLeafs |> Seq.length + //printfn "node count (foo ) = %d" nodeCount + + //printfn "" + //let histo = root + // |> Quadtree.EnumerateNodesBreadthFirst true + // |> Seq.groupBy (fun n -> n.GetType().Name) + // |> Seq.map (fun (key, ns) -> (key, ns |> Seq.length)) + // |> Map.ofSeq + //for kv in histo do printfn "%s -> %d" kv.Key kv.Value //Quadtree.PrintStructure true q