anchor.diff:          ASet.AdaptiveSet(fun () -> new CustomReader<'a>(f) :> IReader<_>) :> aset<_>
   
   
  +    type WithUpdateReader<'a>(m : IMod<unit>, r : IReader<'a>) =
  +        inherit ASetReaders.AbstractReader<'a>()
  +
  +
  +        override x.ComputeDelta() =
  +            m.GetValue x
  +            r.GetDelta x
  +
  +        override x.Release() =
  +            r.Dispose()
  +
  +        override x.Inputs = Seq.ofList [m; r]
  +
  +
  +    let withUpdater (m : IMod<unit>) (s : aset<'a>) =
  +        ASet.AdaptiveSet(fun () -> new WithUpdateReader<_>(m, s.GetReader()) :> IReader<_>) :> aset<_>
  +
  +
   // ===================================================================================
   // LoD stuff
   // ===================================================================================
  -type Node<'a> =
  +[<CustomEquality; NoComparison>]
  +type Node<'a when 'a : equality> =
       {
           id : 'a
           bounds : Box3d
  
positive.code:                                     
  let sg = 
      Sg.group' [
          Sg.geometrySet IndexedGeometryMode.LineList attributeTypes boxes
  
          Helpers.frustum gridCam gridProj
  
          data.BoundingBox.EnlargedByRelativeEps(0.005)
  
negative.code:         granularity : float
      }
  
      override x.GetHashCode() = x.id.GetHashCode()
      override x.Equals o =
          match o with
              | :? Node<'a> as o -> x.id.Equals(o.id)
              | _ -> false
  
  
  type IDataProvider<'a when 'a : equality> =
      abstract member BoundingBox : Box3d
      abstract member Traverse : (Node<'a> -> bool) -> unit
      abstract member GetData : node : 'a -> count : int -> Async<IndexedGeometry>
  
  
  type DummyDataProvider(root : Box3d) =
      
      interface IDataProvider<Box3d> with
          member x.BoundingBox = root
  
          member x.Traverse f =
              let rec traverse (level : int) (b : Box3d) =
                  let box = b
                  let n = 100.0
                  let node = { id = b; bounds = box; inner = true; granularity = Fun.Cbrt(box.Volume / (n*n*n)) }
  
                  if f node then
                      let center = b.Center
  
                      let children =
                          let l = b.Min
                          let u = b.Max
                          let c = center
                          [
                              Box3d(V3d(l.X, l.Y, l.Z), V3d(c.X, c.Y, c.Z))
                              Box3d(V3d(c.X, l.Y, l.Z), V3d(u.X, c.Y, c.Z))
                              Box3d(V3d(l.X, c.Y, l.Z), V3d(c.X, u.Y, c.Z))
                              Box3d(V3d(c.X, c.Y, l.Z), V3d(u.X, u.Y, c.Z))
                              Box3d(V3d(l.X, l.Y, c.Z), V3d(c.X, c.Y, u.Z))
                              Box3d(V3d(c.X, l.Y, c.Z), V3d(u.X, c.Y, u.Z))
                              Box3d(V3d(l.X, c.Y, c.Z), V3d(c.X, u.Y, u.Z))
                              Box3d(V3d(c.X, c.Y, c.Z), V3d(u.X, u.Y, u.Z))
                          ]
  
                      children |> List.iter (traverse (level + 1))
                  else
                      ()
              traverse 0 root
  
          member x.GetData (cell : Box3d) (count : int) =
              async {
                  return IndexedGeometry()
              }
  