All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.sciss.lucre.data.SkipOctree.scala Maven / Gradle / Ivy

Go to download

Extension of Scala-STM, adding optional durability layer, and providing API for confluent and reactive event layers

There is a newer version: 4.6.6
Show newest version
/*
 *  SkipOctree.scala
 *  (Lucre 4)
 *
 *  Copyright (c) 2009-2022 Hanns Holger Rutz. All rights reserved.
 *
 *  This software is published under the GNU Affero General Public License v3+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.lucre
package data

import de.sciss.lucre.geom.{DistanceMeasure, HyperCube, QueryShape, Space}
import de.sciss.serial.{DataInput, TFormat, WritableFormat}

import scala.collection.immutable.{IndexedSeq => Vec}

object SkipOctree {
  implicit def nonTxnPointView[P, A](implicit view: A => P): (A, Any) => P =
    (a, _) => view(a)

  def empty[T <: Exec[T], P, H <: HyperCube[P, H], A](hyperCube: H)
                                      (implicit tx: T, pointView: (A, T) => P, space: Space[P, H],
                                       keyFormat: TFormat[T, A]): SkipOctree[T, P, H, A] =
    DetSkipOctree.empty[T, P, H, A](hyperCube)

  def read[T <: Exec[T], P, H <: HyperCube[P, H], A](in: DataInput)
                                                         (implicit tx: T, pointView: (A, T) => P,
                                                          space: Space[P, H],
                                                          keyFormat: TFormat[T, A]): SkipOctree[T, P, H, A] =
    DetSkipOctree.read[T, P, H, A](in)

  implicit def format[T <: Exec[T], P,
    H <: HyperCube[P, H], A](implicit view: A => P, space: Space[P, H],
                              keyFormat: TFormat[T, A]): TFormat[T, SkipOctree[T, P, H, A]] =
    new Fmt[T, P, H, A]

  private final class Fmt[T <: Exec[T], P, H <: HyperCube[P, H], A](implicit view: A => P, space: Space[P, H],
                                                                         keyFormat: TFormat[T, A])
    extends WritableFormat[T, SkipOctree[T, P, H, A]] {

    override def readT(in: DataInput)(implicit tx: T): SkipOctree[T, P, H, A] =
      DetSkipOctree.read(in)

    override def toString = "SkipOctree.format"
  }
}

/** A `SkipOctree` is a multi-dimensional data structure that
 * maps coordinates to values. It extends the interface
 * of Scala's mutable `Map` and adds further operations such
 * as range requires and nearest neighbour search.
 */
trait SkipOctree[T <: Exec[T], P, H, A] extends Mutable[T] {
  /** The space (i.e., resolution and dimensionality) underlying the tree. */
  def space: Space[P, H]

  /** A function which maps an element (possibly through transactional access) to a geometric point coordinate. */
  def pointView: (A, T) => P

  /** The base square of the tree. No point can lie outside this square (or hyper-cube). */
  def hyperCube: H

  /** Reports the number of decimation levels in the tree. */
  def numLevels(implicit tx: T): Int

  /** The number of orthants in each hyperCube. This is equal
   * to `1 << numDimensions` and gives the upper bound
   * of the index to `QNode.child()`.
   */
  def numOrthants: Int

  /** Queries the element at a given point.
   *
   * @param point  the point to look up.
   * @return `Some` element if found, `None` if the point was not present.
   */
  def get(point: P)(implicit tx: T): Option[A]

  /** Queries whether an element is stored at a given point.
   *
   * @param point  the point to query
   * @return   `true` if an element is associated with the query point, `false` otherwise
   */
  def isDefinedAt(point: P)(implicit tx: T): Boolean

  /** Removes the element stored under a given point view.
   *
   * @param   point the location of the element to remove
   * @return  the element removed, wrapped as `Some`, or `None` if no element was
   *          found for the given point.
   */
  def removeAt(point: P)(implicit tx: T): Option[A]

  /** Queries the number of leaves in the tree. This may be a very costly action,
   * so it is recommended to only use it for debugging purposes.
   */
  def size(implicit tx: T): Int

  /** Adds an element to the tree (or replaces a given element with the same point location).
   *
   * @param   elem  the element to add
   * @return  `true` if the element is new in the tree. If a previous entry with the
   *          same point view is overwritten, this is `true` if the elements were
   *          '''not equal''', `false` if they were equal
   */
  def add(elem: A)(implicit tx: T): Boolean

  /** Looks up a point and applies a transformation to the entry associated with it.
   * This can be used to update an element in-place, or used for maintaining a spatial multi-map.
   *
   * @param point   the location at which to perform the transformation
   * @param fun  a function to transform the element found, or generate a new element. The argument is the
   *             element previously stored with the point, or `None` if no element is found. The result is
   *             expected to be `Some` new element to be stored, or `None` if no element is to be stored
   *             (in this case, if an element was previously stored, it is removed)
   * @return     the previously stored element (if any)
   */
  def transformAt(point: P)(fun: Option[A] => Option[A])(implicit tx: T): Option[A]

  /** Removes an element from the tree
   *
   * @param   elem  the element to remove
   * @return  `true` if the element had been found in the tree and thus been removed.
   */
  def remove(elem: A)(implicit tx: T): Boolean

  /** Adds an element to the tree (or replaces a given element with the same point location).
   *
   * @param   elem  the element to add to the tree
   * @return  the old element stored for the same point view, if it existed
   */
  def update(elem: A)(implicit tx: T): Option[A]

  def rangeQuery[Area](qs: QueryShape[Area, P, H])(implicit tx: T): Iterator[A]

  /** Tests whether the tree contains an element. */
  def contains(elem: A)(implicit tx: T): Boolean

  /** Tests whether the tree is empty (`true`) or whether it contains any elements (`false`). */
  def isEmpty(implicit tx: T): Boolean

  /** Converts the tree into a linearized indexed sequence. This is not necessarily a
   * very efficient method, and should usually just be used for debugging.
   */
  def toIndexedSeq(implicit tx: T): Vec[A]

  /** Converts the tree into a linearized list. This is not necessarily a
   * very efficient method, and should usually just be used for debugging.
   */
  def toList(implicit tx: T): List[A]

  /** Converts the tree into a linearized sequence. This is not necessarily a
   * very efficient method, and should usually just be used for debugging.
   * To avoid surprises, this does not call `iterator.toSeq` because that would
   * produce a `Stream` and thus subject to further changes to the tree while
   * traversing. The returned seq instead is 'forced' and thus stable.
   */
  def toSeq(implicit tx: T): Seq[A]

  /** Converts the tree into a non-transactional set. This is not necessarily a
   * very efficient method, and should usually just be used for debugging.
   */
  def toSet(implicit tx: T): Set[A]

  def clear()(implicit tx: T): Unit

  /** Reports the nearest neighbor entry with respect to
   * a given point.
   *
   * Note: There is a potential numeric overflow if the
   * squared distance of the query point towards the
   * furthest corner of the tree's root hyper-cube exceeds 63 bits.
   * For a root `IntSquare(0x40000000, 0x40000000, 0x40000000)`, this
   * happens for example for any point going more towards north-west
   * than `IntPoint2DLike(-1572067139, -1572067139)`.
   *
   * @param   point the point of which the nearest neighbor is to be found
   * @param   metric   (description missing)
   *
   * @throws  NoSuchElementException  if the tree is empty
   */
  def nearestNeighbor[M](point: P, metric: DistanceMeasure[M, P, H])(implicit tx: T): A

  /** Same as `nearestNeighbor` but returning an `Option`, thus not throwing an exception
   * if no neighbor is found.
   */
  def nearestNeighborOption[M](point: P, metric: DistanceMeasure[M, P, H])(implicit tx: T): Option[A]

  /** An `Iterator` which iterates over the points stored
   * in the octree, using an in-order traversal directed
   * by the orthant indices of the nodes of the tree.
   *
   * Great care has to be taken as the iterator might be corrupted if the tree
   * is successively changed before the iterator is exhausted.
   */
  def iterator(implicit tx: T): Iterator[A]

  /** Adds an element to the tree. If there is already an element stored at the point represented by the
   * new element, it will be replaced. */
  def +=(elem: A)(implicit tx: T): this.type

  /** Removes an element from the tree. If the element is not found, this operation does nothing. */
  def -=(elem: A)(implicit tx: T): this.type

  /** Returns a string debug representation of the octree. */
  def debugPrint()(implicit tx: T): String
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy