diff --git a/src/main/java/net/imglib2/KDTree.java b/src/main/java/net/imglib2/KDTree.java
index 46bd1da70..8aae46b91 100644
--- a/src/main/java/net/imglib2/KDTree.java
+++ b/src/main/java/net/imglib2/KDTree.java
@@ -1,177 +1,32 @@
-/*
- * #%L
- * ImgLib2: a general-purpose, multidimensional image processing library.
- * %%
- * Copyright (C) 2009 - 2023 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld,
- * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke,
- * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner,
- * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert,
- * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin,
- * Jean-Yves Tinevez and Michael Zinsmaier.
- * %%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- * #L%
- */
-
package net.imglib2;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Comparator;
import java.util.List;
-import java.util.ListIterator;
-
-import net.imglib2.util.KthElement;
-
-/**
- * KDTree to access values at RealLocalizable positions.
- *
- * @param
+ * Positions are stored as {@link KDTreePositions} as either + * {@link KDTreePositions.Flat} or {@link KDTreePositions.Nested}. + *
+ * Values (of type {@code T}) are stored as {@link KDTreeValues} either a
+ * 1D {@code RandomAccessibleInterval
+ * {@link #values()} returns all values as a 1D {@code
+ * RandomAccessibleInterval
+ * The nodes in the tree are arranged in Eytzinger layout (children of i are at
+ * 2i and 2i+1). Additionally, pivot indices are chosen such that "leaf layers"
+ * are filled from the left.
+ *
+ * For example 10 nodes will always be arranged like this:
+ *
+ * Currently, there are two implementations:
+ * - {@link Nested} stores the positions as a {@code double[][]} where {@code
+ * positions[d][i]} is dimension {@code d} of the {@code i}-th point. This
+ * allows for a total of 2^31-8 nodes but doesn't keep the positions
+ * contiguous in memory.
+ * - {@link Flat} stores the positions as a {@code double[]}where {@code
+ * positions[d + i*n]} is dimension {@code d} of the {@code i}-th point,
+ * with {@code n} the number of dimensions. This means that the positions
+ * are contiguous in memory but the number of nodes is limited to (2^31-8)/n.
+ *
+ * {@link #getNestedPositions()} returns positions in nested {@code double[][]}
+ * (which is created if class is {@link Flat}). {@link #getFlatPositions()}
+ * returns flat {@code double[]} if class is {@link Flat}, otherwise {@code null}.
+ */
+public abstract class KDTreePositions {
+
+ protected final int numDimensions;
+
+ protected final int numPoints;
+ private volatile RealInterval boundingBox;
+
+ static class Nested extends KDTreePositions {
+ private final double[][] positions;
+
+ Nested(final double[][] positions) {
+ super(positions.length, positions[0].length);
+ this.positions = positions;
+ }
+
+ @Override public double get(final int i, final int d) {
+ return positions[d][i];
+ }
+
+ @Override
+ public double[] getFlatPositions() {
+ // positions in this case might be too large to fit in a single array
+ return null;
+ }
+
+ @Override
+ public double[][] getNestedPositions() {
+ return positions;
+ }
+
+ @Override
+ protected RealInterval createBoundingBox() {
+ final double[] min = new double[numDimensions];
+ final double[] max = new double[numDimensions];
+ KDTreeUtils.computeMinMax(positions, min, max);
+ return FinalRealInterval.wrap(min, max);
+ }
+ }
+
+ static class Flat extends KDTreePositions {
+ private final double[] positions;
+
+ Flat(final double[] positions, final int numDimensions) {
+ super(numDimensions, positions.length / numDimensions);
+ this.positions = positions;
+ }
+
+ @Override
+ public double get(final int i, final int d) {
+ return positions[numDimensions * i + d];
+ }
+
+ @Override
+ public double[] getFlatPositions() {
+ return positions;
+ }
+
+ @Override
+ public double[][] getNestedPositions() {
+ return KDTreeUtils.unflatten(positions, numDimensions);
+ }
+
+ @Override
+ protected RealInterval createBoundingBox() {
+ final double[] min = new double[numDimensions];
+ final double[] max = new double[numDimensions];
+ KDTreeUtils.computeMinMax(positions, min, max);
+ return FinalRealInterval.wrap(min, max);
+ }
+ }
+
+ KDTreePositions(final int numDimensions, final int numPoints) {
+ this.numDimensions = numDimensions;
+ this.numPoints = numPoints;
+ }
+
+ /**
+ * Get the coordinates of the node {@code i} in dimension {@code d}.
+ *
+ * @return the coordinate
+ */
+ public abstract double get(final int i, final int d);
+
+ /**
+ * Get positions of points in the tree as a flat {@code double[]} array
+ * where {@code positions[d + i*n]} is dimension {@code d} of the {@code i}-th
+ * point.
+ *
+ * For serialisation and usage by the tree.
+ *
+ * Internal storage may be nested in a {@code double[][]} array. In
+ * this case, there may be too many points to fit in a single {@code
+ * double[]} array, so {@code null} is returned.
+ */
+ public abstract double[] getFlatPositions();
+
+ /**
+ * Get positions of points in the tree as a nested {@code double[][]} array
+ * where {@code positions[d][i]} is dimension {@code d} of the {@code i}-th
+ * point.
+ *
+ * For serialisation and usage by the tree.
+ *
+ * Internal storage may be flattened into single {@code double[]} array. In
+ * this case, the nested {@code double[][]} array is created here.
+ */
+ public abstract double[][] getNestedPositions();
+
+ protected abstract RealInterval createBoundingBox();
+
+ public int numDimensions() {
+ return numDimensions;
+ }
+
+ public int numPoints() {
+ return numPoints;
+ }
+
+ public int size() {
+ return numPoints;
+ }
+
+ public RealInterval boundingBox() {
+ if (boundingBox == null)
+ boundingBox = createBoundingBox();
+ return boundingBox;
+ }
+
+ public static KDTreePositions create(final double[][] positions) {
+ return new Nested( positions );
+ }
+
+ public static KDTreePositions create(final double[] positions, final int numDimensions) {
+ return new Flat(positions, numDimensions);
+ }
+}
diff --git a/src/main/java/net/imglib2/kdtree/KDTreeUtils.java b/src/main/java/net/imglib2/kdtree/KDTreeUtils.java
new file mode 100644
index 000000000..c972ff12b
--- /dev/null
+++ b/src/main/java/net/imglib2/kdtree/KDTreeUtils.java
@@ -0,0 +1,573 @@
+package net.imglib2.kdtree;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import net.imglib2.RandomAccess;
+import net.imglib2.RealLocalizable;
+import net.imglib2.img.Img;
+import net.imglib2.img.array.ArrayImgFactory;
+import net.imglib2.type.NativeType;
+
+final class KDTreeUtils
+{
+ static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+ /**
+ * If the tree is flattened into an array the left child of node at
+ * index {@code i} has index {@code 2 * i + 1}.
+ */
+ static int leftChildIndex( final int i )
+ {
+ return 2 * i + 1;
+ }
+
+ /**
+ * If the tree is flattened into an array the right child of node at
+ * index {@code i} has index {@code 2 * i + 2}.
+ */
+ static int rightChildIndex( final int i )
+ {
+ return 2 * i + 2;
+ }
+
+ /**
+ * If the tree is flattened into an array the parent of node at
+ * index {@code i} has index {@code (i - 1) / 2} (except for the
+ * root node {@code i==0}).
+ */
+ static int parentIndex( final int i )
+ {
+ return ( i - 1 ) / 2;
+ }
+
+ /**
+ * Copy the coordinates of the given {@code points} to a new {@code double[][] positions} array.
+ * The coordinate in dimension {@code d} of the {@code i}th point is stored at {@code positions[d][i]}.
+ * That is, {@code positions[0]} has all X coordinates, {@code positions[0]} has all Y coordinates, and so on.
+ */
+ static double[][] initPositions(
+ final int numDimensions,
+ final int numPoints,
+ final Iterable< ? extends RealLocalizable > points )
+ {
+ final double[][] positions = new double[ numDimensions ][ numPoints ];
+ final Iterator< ? extends RealLocalizable > ipos = points.iterator();
+ for ( int i = 0; i < numPoints; ++i )
+ {
+ if ( !ipos.hasNext() )
+ throw new IllegalArgumentException( "positions Iterable is empty" );
+ final RealLocalizable pos = ipos.next();
+ for ( int d = 0; d < numDimensions; d++ )
+ positions[ d ][ i ] = pos.getDoublePosition( d );
+ }
+ return positions;
+ }
+
+ /**
+ * Sort the given points into a k-d tree.
+ *
+ * The tree is given as a flat (heap-like) array of point indices, {@code int[] tree}.
+ * The index of the point chosen as the root node is {@code tree[0]}.
+ * The coordinates of the root node are at {@code positions[d][tree[0]]}.
+ *
+ * The indices of the children (less-or-equal and greater-or-equal, respectively) of root are at {@code tree[1]} and {@code tree[2]}, and so on.
+ *
+ * @param positions
+ * The coordinates for the {@code i}th point are stored at {@code positions[d][i]} where {@code d} is the dimension.
+ * See {@link #initPositions(int, int, Iterable)}.
+ *
+ * @return flattened tree of point indices
+ */
+ static int[] makeTree( double[][] positions )
+ {
+ return new MakeTree( positions ).tree;
+ }
+
+ /**
+ * Re-order the node {@code positions} to form a tree corresponding to the index array {@code tree'={0,1,2,...}}.
+ *
+ * @param positions
+ * @param tree
+ *
+ * @return
+ */
+ static double[][] reorder( double[][] positions, int[] tree )
+ {
+ final int numDimensions = positions.length;
+ final int numPoints = positions[ 0 ].length;
+ assert tree.length == numPoints;
+ final double[][] reordered = new double[ numDimensions ][];
+ Arrays.setAll( reordered, d -> reorder( positions[ d ], tree ) );
+ return reordered;
+ }
+
+ /**
+ * Create a new {@code double[]} array that contains the elements of {@code
+ * values}, ordered such that {@code values[order[i]]} is at index {@code i}.
+ */
+ static double[] reorder( final double[] values, final int[] order )
+ {
+ final int size = order.length;
+ final double[] reordered = new double[ size ];
+ Arrays.setAll( reordered, i -> values[ order[ i ] ] );
+ return reordered;
+ }
+
+ /**
+ * Create a new {@code int[]} array that contains the elements of {@code
+ * values}, ordered such that {@code values[order[i]]} is at index {@code i}.
+ */
+ static int[] reorder( final int[] values, final int[] order )
+ {
+ final int size = order.length;
+ final int[] reordered = new int[ size ];
+ Arrays.setAll( reordered, i -> values[ order[ i ] ] );
+ return reordered;
+ }
+
+ /**
+ * Re-order the node {@code positions} to form a tree corresponding to the index array {@code tree={0,1,2,...}}.
+ * Then flatten the result into a 1-D array, interleaving coordinates in all dimensions.
+ *
+ * @param positions
+ * @param tree
+ *
+ * @return
+ */
+ static double[] reorderToFlatLayout( final double[][] positions, final int[] tree )
+ {
+ final int numDimensions = positions.length;
+ final int numPoints = positions[ 0 ].length;
+ assert tree.length == numPoints;
+ if ( ( long ) numDimensions * numPoints > MAX_ARRAY_SIZE )
+ throw new IllegalArgumentException( "positions[][] is too large to be stored in a flat array" );
+ final double[] reordered = new double[ numDimensions * numPoints ];
+ for ( int i = 0; i < numPoints; ++i )
+ for ( int d = 0; d < numDimensions; ++d )
+ reordered[ numDimensions * i + d ] = positions[ d ][ tree[ i ] ];
+ return reordered;
+ }
+
+ /**
+ * Flatten the nested {@code positions} array.
+ *
+ * @param positions positions in nested layout
+ * @return positions in flattened layout
+ */
+ static double[] flatten( double[][] positions )
+ {
+ final int numDimensions = positions.length;
+ final int numPoints = positions[ 0 ].length;
+ final double[] flattened = new double[ numDimensions * numPoints ];
+ for ( int i = 0; i < numPoints; ++i )
+ for ( int d = 0; d < numDimensions; ++d )
+ flattened[ numDimensions * i + d ] = positions[ d ][ i ];
+ return flattened;
+
+ }
+
+ /**
+ * Transform flat {@code positions} array into a nested {@code
+ * double[numDimensions][numPoints]} array.
+ *
+ * With flat layout, positions are stored as a flat {@code double[]} array,
+ * where {@code positions[d + i*n]} is dimension {@code d} of the {@code
+ * i}-th point, with {@code n} the number of dimensions.
+ *
+ * With nested layout, positions are stored as a nested {@code double[][]}
+ * array where {@code positions[d][i]} is dimension {@code d} of the {@code
+ * i}-th point.
+ *
+ * @param positions
+ * positions in flattened layout
+ * @param n
+ * number of dimensions
+ *
+ * @return positions in nested layout
+ */
+ static double[][] unflatten( double[] positions, final int n )
+ {
+ final int numPoints = positions.length / n;
+ final double[][] unflattened = new double[ n ][ numPoints ];
+ for (int i = 0; i < positions.length; ++i )
+ {
+ final int d = i % n;
+ unflattened[ d ][ i / n + d ] = positions[ i ];
+ }
+ return unflattened;
+
+ }
+
+ /**
+ * Compute bounding box of positions in nested layout
+ */
+ static void computeMinMax(final double[][] positions, final double[] min, final double[] max) {
+ final int n = min.length;
+ for (int d = 0; d < n; d++) {
+ double maxd = Double.NEGATIVE_INFINITY;
+ double mind = Double.POSITIVE_INFINITY;
+ for (double v : positions[d]) {
+ if (v < mind) {
+ mind = v;
+ }
+ if (v > maxd) {
+ maxd = v;
+ }
+ }
+ min[d] = mind;
+ max[d] = maxd;
+ }
+ }
+
+ /**
+ * Compute bounding box of positions in flat layout
+ */
+ static void computeMinMax(final double[] flatPositions, final double[] min, final double[] max) {
+ final int n = min.length;
+ Arrays.fill(max, Double.NEGATIVE_INFINITY);
+ Arrays.fill(min, Double.POSITIVE_INFINITY);
+ int d = 0;
+ for (double v : flatPositions) {
+ if (v < min[d]) {
+ min[d] = v;
+ }
+ if (v > max[d]) {
+ max[d] = v;
+ }
+ d = (d + 1) % n;
+ }
+ }
+
+ /**
+ * Invert the given permutation {@code tree}.
+ *
+ * For example, {@code tree = {3, 4, 1, 0, 5, 2}} indicates that coordinates
+ * and value for the node at heap index {@code i} can be found at index
+ * {@code tree[i]} in the respective input list.
+ *
+ * The inverse, {@code inv = {3, 4, 1, 0, 5, 2}} indicates that coordinates
+ * and value at index {@code i} in the respective input list belong to the
+ * node at heap index {@code inv[i]}.
+ *
+ * @param tree a permutation
+ * @return the inverse permutation
+ */
+ static int[] invert( int[] tree )
+ {
+ // For example:
+ // i = 0 1 2 3 4 5
+ // tree = {3, 4, 1, 0, 5, 2}
+ // output = {3, 2, 5, 0, 1, 4}
+
+ final int[] inv = new int[ tree.length ];
+ for ( int i = 0; i < tree.length; i++ )
+ inv[tree[i]] = i;
+ return inv;
+ }
+
+ /**
+ * Re-order the node {@code values} to form a tree corresponding to the index array {@code tree'={0,1,2,...}}.
+ * The tree is given as an {@link #invert(int[]) inverted permutation}, so that we can iterate through the {@code values} in order, putting each at the right index in the returned {@code List}.
+ *
+ * @param invtree
+ * @param values
+ * @param
+ * A pivot element is chosen by median-of-three method. Then {@code
+ * [i,j]} is reordered, such that all elements before the pivot are
+ * smaller-equal and all elements after the pivot are larger-equal the
+ * pivot. The index of the pivot element is returned.
+ *
+ * @param i
+ * index of first element of the sublist
+ * @param j
+ * index of last element of the sublist
+ * @param values
+ * the array of values of list elements.
+ * @param order
+ * order of list elements. E.g., {@code order[0]=3} means that {@code values[3]} is the first element of the list.
+ * @return index of pivot element
+ */
+ static int partition( int i, int j, final double[] values, final int[] order )
+ {
+ final int len = j - i + 1;
+ if ( len <= 2 )
+ {
+ if ( len <= 0 )
+ throw new IllegalArgumentException();
+ if ( values[ order[ i ] ] > values[ order[ j ] ] )
+ swap( i, j, order );
+ return i;
+ }
+ else
+ {
+ final int m = ( i + j ) / 2;
+ if ( values[ order[ i ] ] > values[ order[ m ] ] )
+ swap( i, m, order );
+ if ( values[ order[ i ] ] > values[ order[ j ] ] )
+ swap( i, j, order );
+ if ( values[ order[ m ] ] > values[ order[ j ] ] )
+ swap( m, j, order );
+ swap( m, i + 1, order );
+ final int p = ++i;
+ final double pivot = values[ order[ p ] ];
+ while ( true )
+ {
+ while ( values[ order[ ++i ] ] < pivot )
+ ;
+ while ( values[ order[ --j ] ] > pivot )
+ ;
+ if ( j < i )
+ break;
+ swap( i, j, order );
+ }
+ swap( p, j, order );
+ return j;
+ }
+ }
+
+ /**
+ * Sort a sublist. The list is given by an immutable array of {@code
+ * values}, and an index array that represents the {@code order} of values
+ * in the list. This method only rearranges the {@code order} array.
+ *
+ * @param i
+ * index of first element of the sublist
+ * @param j
+ * index of last element of the sublist
+ * @param values
+ * the array of values of list elements.
+ * @param order
+ * order of list elements. E.g., {@code order[0]=3} means that {@code values[3]} is the first element of the list.
+ */
+ static void quicksort( final int i, final int j, final double[] values, final int[] order )
+ {
+ if ( 0 <= i && i < j )
+ {
+ final int p = partition( i, j, values, order );
+ quicksort( i, p - 1, values, order );
+ quicksort( p + 1, j, values, order );
+ }
+ }
+
+ private static final class MakeTree
+ {
+ private final int numDimensions;
+
+ private final int numPoints;
+
+ /**
+ * The coordinates for the {@code i}th point are stored at {@code positions[d][k]} where {@code d} is the dimension.
+ */
+ private final double[][] positions;
+
+ /**
+ * Temporary array to keep track of elements.
+ * Initialized to {@code 0, 1, ... } and then permuted when sorting the elements into a tree.
+ */
+ private final int[] indices;
+
+ /**
+ * Node indices in a flattened (heap-like) array.
+ * For example: the index of the root node is {@code tree[0]}.
+ * The coordinates of the root node are at {@code positions[d][tree[0]]} where {@code d} is the dimension.
+ * The children of the root node are at {@code tree[1]} and {@code tree[2]}, and so on.
+ */
+ private final int[] tree;
+
+ private MakeTree( final double[][] positions )
+ {
+ this.positions = positions;
+ numDimensions = positions.length;
+ numPoints = positions[ 0 ].length;
+ indices = new int[ numPoints ];
+ tree = new int[ numPoints ];
+ Arrays.setAll( indices, j -> j );
+ makeNode( 0, numPoints - 1, 0, 0 );
+ }
+
+ /**
+ * Calculate pivot index such that the tree will be arranged in a way that
+ * "leaf layers" are filled from the left.
+ * For example 10 nodes will always be arranged like this:
+ *
+ * Values (of type {@code T}) are stored as either a 1D {@code
+ * RandomAccessibleInterval
+ * {@link #values()} returns all values as a 1D {@code
+ * RandomAccessibleInterval
+ * 0
+ * / \
+ * 1 2
+ * / \ / \
+ * 3 4 5 6
+ * / \ /
+ * 7 8 9
+ *
+ *
+ * never like this:
+ *
+ * 0
+ * / \
+ * 1 2
+ * / \ / \
+ * 3 4 5 6
+ * / / /
+ * 7 8 9
+ *
+ *
+ * By choosing pivots in this way, the tree structure is fully
+ * determined. For every node index, the child indices can be calculated
+ * without dependent reads. And iff the calculated child index is less
+ * than the number of nodes, the child exists.
+ */
+public class KDTreeImpl
+{
+ final int numDimensions;
+
+ private final int numPoints;
+
+ protected final KDTreePositions positions;
+
+ public KDTreeImpl(final KDTreePositions positions) {
+ this.numDimensions = positions.numDimensions;
+ this.numPoints = positions.numPoints;
+ this.positions = positions;
+ }
+
+ /**
+ * Get the root node of the tree.
+ *
+ * @return index of the root node
+ */
+ public int root()
+ {
+ return 0;
+ }
+
+ /**
+ * Get the left child of node {@code i}.
+ *
+ * @param i
+ * node index
+ *
+ * @return index of left child or {@code -1} if no left child exists
+ */
+ public int left( final int i )
+ {
+ return ifExists( leftChildIndex( i ) );
+ }
+
+ /**
+ * Get the right child of node {@code i}.
+ *
+ * @param i
+ * node index
+ *
+ * @return index of right child or {@code -1} if no right child exists
+ */
+ public int right( final int i )
+ {
+ return ifExists( rightChildIndex( i ) );
+ }
+
+ /**
+ * Get the parent of node {@code i}.
+ *
+ * @param i
+ * node index
+ *
+ * @return index of parent
+ */
+ public int parent( final int i )
+ {
+ return i == root() ? -1 : parentIndex( i );
+ }
+
+ /**
+ * If a node with index {@code i} exists, returns {@code i}.
+ * Otherwise, returns {@code -1}.
+ */
+ private int ifExists( final int i )
+ {
+ return i < numPoints ? i : -1;
+ }
+
+ /**
+ * Get the dimension along which node {@code i} divides the space.
+ *
+ * @param i
+ * node index
+ *
+ * @return splitting dimension.
+ */
+ public int splitDimension( final int i )
+ {
+ return ( 31 - Integer.numberOfLeadingZeros( i + 1 ) ) % numDimensions;
+ }
+
+ public double getDoublePosition(final int i, final int d) {
+ return positions.get(i, d);
+ }
+
+ /**
+ * Compute the squared distance from node {@code i} to {@code pos}.
+ */
+ public float squDistance( final int i, final float[] pos )
+ {
+ float sum = 0;
+ for ( int d = 0; d < numDimensions; ++d )
+ {
+ final float diff = pos[ d ] - ( float ) positions.get(i, d);
+ sum += diff * diff;
+ }
+ return sum;
+ }
+
+ /**
+ * Compute the squared distance from node {@code i} to {@code pos}.
+ */
+ public double squDistance( final int i, final double[] pos )
+ {
+ double sum = 0;
+ for ( int d = 0; d < numDimensions; ++d )
+ {
+ final double diff = pos[ d ] - positions.get(i, d);
+ sum += diff * diff;
+ }
+ return sum;
+ }
+
+ /**
+ * Compute the squared distance from node {@code i} to {@code pos}.
+ */
+ public double squDistance( final int i, final RealLocalizable pos )
+ {
+ double sum = 0;
+ for ( int d = 0; d < numDimensions; ++d )
+ {
+ final double diff = pos.getDoublePosition( d ) - positions.get(i, d);
+ sum += diff * diff;
+ }
+ return sum;
+ }
+
+ public int numDimensions()
+ {
+ return numDimensions;
+ }
+
+ public int size()
+ {
+ return numPoints;
+ }
+
+ public int depth()
+ {
+ return 32 - Integer.numberOfLeadingZeros( numPoints );
+ }
+}
diff --git a/src/main/java/net/imglib2/kdtree/KDTreePositions.java b/src/main/java/net/imglib2/kdtree/KDTreePositions.java
new file mode 100644
index 000000000..8d8acc6db
--- /dev/null
+++ b/src/main/java/net/imglib2/kdtree/KDTreePositions.java
@@ -0,0 +1,159 @@
+package net.imglib2.kdtree;
+
+import net.imglib2.FinalRealInterval;
+import net.imglib2.RealInterval;
+
+
+/**
+ * Stores the positions of the nodes in a KDTree and provides access to them.
+ *
+ * 0
+ * / \
+ * 1 2
+ * / \ / \
+ * 3 4 5 6
+ * / \ /
+ * 7 8 9
+ *
+ *
+ * never like this:
+ *
+ * 0
+ * / \
+ * 1 2
+ * / \ / \
+ * 3 4 5 6
+ * / / /
+ * 7 8 9
+ *
+ *
+ * By choosing pivots in this way, the tree structure is fully
+ * determined. For every node index, the child indices can be calculated
+ * without dependent reads. And iff the calculated child index is less
+ * than the number of nodes, the child exists.
+ */
+ private static int pivot( final int len )
+ {
+ final int h = Integer.highestOneBit( len );
+ final int h2 = h >> 1;
+ return ( len - h >= h2 )
+ ? h - 1
+ : len - h2;
+ }
+
+ private void makeNode( final int i, final int j, final int d, final int nodeIndex )
+ {
+ if ( j > i )
+ {
+ final int k = i + pivot( j - i + 1 );
+ kthElement( i, j, k, d );
+ tree[ nodeIndex ] = indices[ k ];
+ final int dChild = ( d + 1 ) % numDimensions;
+ makeNode( i, k - 1, dChild, leftChildIndex( nodeIndex ) );
+ makeNode( k + 1, j, dChild, rightChildIndex( nodeIndex ) );
+ }
+ else if ( j == i )
+ {
+ tree[ nodeIndex ] = indices[ i ];
+ }
+ }
+
+ /**
+ * Partition a sublist of Nodes by their coordinate in the specified
+ * dimension, such that the k-th smallest value is at position {@code
+ * k}, elements before the k-th are smaller or equal and elements after
+ * the k-th are larger or equal.
+ *
+ * @param i
+ * index of first element of the sublist
+ * @param j
+ * index of last element of the sublist
+ * @param k
+ * index for k-th smallest value. {@code i <= k <= j}.
+ * @param compare_d
+ * dimension by which to order the sublist
+ */
+ private void kthElement( int i, int j, final int k, final int compare_d )
+ {
+ while ( true )
+ {
+ final int pivotpos = partition( i, j, positions[ compare_d ], indices );
+ if ( pivotpos > k )
+ {
+ // partition lower half
+ j = pivotpos - 1;
+ }
+ else if ( pivotpos < k )
+ {
+ // partition upper half
+ i = pivotpos + 1;
+ }
+ else
+ return;
+ }
+ }
+ }
+
+ private KDTreeUtils() {}
+}
diff --git a/src/main/java/net/imglib2/kdtree/KDTreeValues.java b/src/main/java/net/imglib2/kdtree/KDTreeValues.java
new file mode 100644
index 000000000..74c476657
--- /dev/null
+++ b/src/main/java/net/imglib2/kdtree/KDTreeValues.java
@@ -0,0 +1,134 @@
+package net.imglib2.kdtree;
+
+import net.imglib2.RandomAccess;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.img.list.ListImg;
+import net.imglib2.util.Util;
+
+import java.util.List;
+import java.util.function.IntFunction;
+import java.util.function.Supplier;
+
+/**
+ * Stores the KDTree values.
+ * KDTreeValues create(final RandomAccessibleInterval values) {
+ return new KDTreeValues.ImgValues<>(values);
+ }
+
+ public static KDTreeValues create(final List values) {
+ return new KDTreeValues.ListValues<>(values);
+ }
+}
diff --git a/src/main/java/net/imglib2/kdtree/KNearestNeighborSearchImpl.java b/src/main/java/net/imglib2/kdtree/KNearestNeighborSearchImpl.java
new file mode 100644
index 000000000..fccec79bb
--- /dev/null
+++ b/src/main/java/net/imglib2/kdtree/KNearestNeighborSearchImpl.java
@@ -0,0 +1,125 @@
+package net.imglib2.kdtree;
+
+import java.util.Arrays;
+import net.imglib2.RealLocalizable;
+
+/**
+ * k-nearest-neighbor search on {@link KDTreeImpl}.
+ * Results are node indices.
+ */
+public class KNearestNeighborSearchImpl
+{
+ private final KDTreeImpl tree;
+ private final int numDimensions;
+ private final int numPoints;
+ private final double[] pos;
+ private final int k;
+ private final double[] bestSquDistance;
+ private final int[] bestIndex;
+ private final double[] axisDiffs;
+ private final int[] awayChilds;
+
+ public KNearestNeighborSearchImpl( final KDTreeImpl tree, final int k )
+ {
+ this.tree = tree;
+ numDimensions = tree.numDimensions();
+ numPoints = tree.size();
+ this.k = k;
+ pos = new double[ numDimensions ];
+ bestSquDistance = new double[ k ];
+ bestIndex = new int[ k ];
+ final int depth = tree.depth();
+ axisDiffs = new double[ depth + 1 ];
+ awayChilds = new int[ depth + 1 ];
+ }
+
+ /**
+ * Insert index into list of best nodes.
+ * Also checks whether index will be inserted at all, that is,
+ * whether squDistance < bestSquDistance[k-1]
+ */
+ private void insert( final double squDistance, final int index )
+ {
+ // first check whether index will be inserted at all
+ if ( squDistance < bestSquDistance[ k - 1 ] )
+ {
+
+ // find insertion point, shifting existing elements to make room
+ int i;
+ for ( i = k - 1; i > 0 && squDistance < bestSquDistance[ i - 1 ]; --i )
+ {
+ bestSquDistance[ i ] = bestSquDistance[ i - 1 ];
+ bestIndex[ i ] = bestIndex[ i - 1 ];
+ }
+
+ // insert index at i,
+ bestSquDistance[ i ] = squDistance;
+ bestIndex[ i ] = index;
+ }
+ }
+
+ public void search( final RealLocalizable p )
+ {
+ p.localize( pos );
+ int current = tree.root();
+ int depth = 0;
+ Arrays.fill( bestSquDistance, Double.POSITIVE_INFINITY );
+ Arrays.fill( bestIndex, -1 );
+ while ( true )
+ {
+ insert( tree.squDistance( current, pos ), current );
+
+ final int d = depth % numDimensions;
+ final double axisDiff = pos[ d ] - tree.getDoublePosition( current, d );
+ final boolean leftIsNearBranch = axisDiff < 0;
+
+ // search the near branch
+ final int nearChild = ( 2 * current ) + ( leftIsNearBranch ? 1 : 2 );
+ final int awayChild = ( 2 * current ) + ( leftIsNearBranch ? 2 : 1 );
+ ++depth;
+ awayChilds[ depth ] = awayChild;
+ axisDiffs[ depth ] = axisDiff * axisDiff;
+ if ( nearChild >= numPoints )
+ {
+ while ( awayChilds[ depth ] >= numPoints || axisDiffs[ depth ] > bestSquDistance[ k - 1 ] )
+ {
+ if ( --depth == 0 )
+ {
+ return;
+ }
+ }
+ current = awayChilds[ depth ];
+ awayChilds[ depth ] = numPoints;
+ }
+ else
+ {
+ current = nearChild;
+ }
+ }
+ }
+
+ public int k()
+ {
+ return k;
+ }
+
+ public int bestIndex( final int i )
+ {
+ return bestIndex[ i ];
+ }
+
+ public double bestSquDistance( final int i )
+ {
+ return bestSquDistance[ i ];
+ }
+
+ public KNearestNeighborSearchImpl copy()
+ {
+ final KNearestNeighborSearchImpl copy = new KNearestNeighborSearchImpl( tree, k );
+ System.arraycopy( pos, 0, copy.pos, 0, pos.length );
+ System.arraycopy( bestIndex, 0, copy.bestIndex, 0, bestIndex.length );
+ System.arraycopy( bestSquDistance, 0, copy.bestSquDistance, 0, bestSquDistance.length );
+ return copy;
+ }
+
+}
diff --git a/src/main/java/net/imglib2/kdtree/NearestNeighborSearchImpl.java b/src/main/java/net/imglib2/kdtree/NearestNeighborSearchImpl.java
new file mode 100644
index 000000000..d887bea94
--- /dev/null
+++ b/src/main/java/net/imglib2/kdtree/NearestNeighborSearchImpl.java
@@ -0,0 +1,95 @@
+package net.imglib2.kdtree;
+
+import net.imglib2.RealLocalizable;
+
+/**
+ * Nearest-neighbor search on {@link KDTreeImpl}.
+ * Results are node indices.
+ */
+public class NearestNeighborSearchImpl
+{
+ private final KDTreeImpl tree;
+ private final int numDimensions;
+ private final int numPoints;
+ private final double[] pos;
+ private int bestIndex;
+ private double bestSquDistance;
+ private final double[] axisDiffs;
+ private final int[] awayChilds;
+
+ public NearestNeighborSearchImpl( final KDTreeImpl tree )
+ {
+ this.tree = tree;
+ numDimensions = tree.numDimensions();
+ numPoints = tree.size();
+ pos = new double[ numDimensions ];
+ bestIndex = -1;
+ bestSquDistance = Double.POSITIVE_INFINITY;
+ final int depth = tree.depth();
+ axisDiffs = new double[ depth + 1 ];
+ awayChilds = new int[ depth + 1 ];
+ }
+
+ public void search( final RealLocalizable p )
+ {
+ p.localize( pos );
+ int current = tree.root();
+ int depth = 0;
+ bestSquDistance = ( bestIndex >= 0 ) ? tree.squDistance( bestIndex, pos ) : Double.POSITIVE_INFINITY;
+ while ( true )
+ {
+ final double squDistance = tree.squDistance( current, pos );
+ if ( squDistance < bestSquDistance )
+ {
+ bestSquDistance = squDistance;
+ bestIndex = current;
+ }
+
+ final int d = depth % numDimensions;
+ final double axisDiff = pos[ d ] - tree.getDoublePosition( current, d );
+ final boolean leftIsNearBranch = axisDiff < 0;
+
+ // search the near branch
+ final int nearChild = ( 2 * current ) + ( leftIsNearBranch ? 1 : 2 );
+ final int awayChild = ( 2 * current ) + ( leftIsNearBranch ? 2 : 1 );
+ ++depth;
+ awayChilds[ depth ] = awayChild;
+ axisDiffs[ depth ] = axisDiff * axisDiff;
+ if ( nearChild >= numPoints )
+ {
+ while ( awayChilds[ depth ] >= numPoints || axisDiffs[ depth ] > bestSquDistance )
+ {
+ if ( --depth == 0 )
+ {
+ return;
+ }
+ }
+ current = awayChilds[ depth ];
+ awayChilds[ depth ] = numPoints;
+ }
+ else
+ {
+ current = nearChild;
+ }
+ }
+ }
+
+ public int bestIndex()
+ {
+ return bestIndex;
+ }
+
+ public double bestSquDistance()
+ {
+ return bestSquDistance;
+ }
+
+ public NearestNeighborSearchImpl copy()
+ {
+ final NearestNeighborSearchImpl copy = new NearestNeighborSearchImpl( tree );
+ System.arraycopy( pos, 0, copy.pos, 0, pos.length );
+ copy.bestIndex = bestIndex;
+ copy.bestSquDistance = bestSquDistance;
+ return copy;
+ }
+}
diff --git a/src/main/java/net/imglib2/kdtree/RadiusNeighborSearchImpl.java b/src/main/java/net/imglib2/kdtree/RadiusNeighborSearchImpl.java
new file mode 100644
index 000000000..8d45f2264
--- /dev/null
+++ b/src/main/java/net/imglib2/kdtree/RadiusNeighborSearchImpl.java
@@ -0,0 +1,155 @@
+package net.imglib2.kdtree;
+
+import java.util.Arrays;
+import net.imglib2.RealLocalizable;
+
+import static net.imglib2.kdtree.KDTreeUtils.quicksort;
+import static net.imglib2.kdtree.KDTreeUtils.reorder;
+
+/**
+ * Radius neighbor search on {@link KDTreeImpl}.
+ * Results are node indices.
+ */
+public class RadiusNeighborSearchImpl
+{
+ private final KDTreeImpl tree;
+ private final int numDimensions;
+ private final int numPoints;
+ private final double[] pos;
+ private final double[] axisDiffs;
+ private final int[] awayChilds;
+ private final Neighbors neighbors;
+
+ public RadiusNeighborSearchImpl( final KDTreeImpl tree )
+ {
+ this.tree = tree;
+ numDimensions = tree.numDimensions();
+ numPoints = tree.size();
+ pos = new double[ numDimensions ];
+ final int depth = tree.depth();
+ axisDiffs = new double[ depth + 1 ];
+ awayChilds = new int[ depth + 1 ];
+ neighbors = new Neighbors();
+ }
+
+ public void search( final RealLocalizable p, final double radius, final boolean sortResults )
+ {
+ assert radius >= 0;
+ final double squRadius = radius * radius;
+ p.localize( pos );
+ neighbors.clear();
+ int current = tree.root();
+ int depth = 0;
+ while ( true )
+ {
+ final double squDistance = tree.squDistance( current, pos );
+ if ( squDistance < squRadius )
+ {
+ neighbors.add( squDistance, current );
+ }
+
+ final int d = depth % numDimensions;
+ final double axisDiff = pos[ d ] - tree.getDoublePosition( current, d );
+ final boolean leftIsNearBranch = axisDiff < 0;
+
+ // search the near branch
+ final int nearChild = ( 2 * current ) + ( leftIsNearBranch ? 1 : 2 );
+ final int awayChild = ( 2 * current ) + ( leftIsNearBranch ? 2 : 1 );
+ ++depth;
+ awayChilds[ depth ] = awayChild;
+ axisDiffs[ depth ] = axisDiff * axisDiff;
+ if ( nearChild >= numPoints )
+ {
+ while ( awayChilds[ depth ] >= numPoints || axisDiffs[ depth ] > squRadius )
+ {
+ if ( --depth == 0 )
+ {
+ if ( sortResults )
+ {
+ neighbors.sort();
+ }
+ return;
+ }
+ }
+ current = awayChilds[ depth ];
+ awayChilds[ depth ] = numPoints;
+ }
+ else
+ {
+ current = nearChild;
+ }
+ }
+ }
+
+ public int numNeighbors()
+ {
+ return neighbors.size;
+ }
+
+ public int bestIndex( final int i )
+ {
+ return neighbors.indices[ i ];
+ }
+
+ public double bestSquDistance( final int i )
+ {
+ return neighbors.distances[ i ];
+ }
+
+ public RadiusNeighborSearchImpl copy()
+ {
+ final RadiusNeighborSearchImpl copy = new RadiusNeighborSearchImpl( tree );
+ System.arraycopy( pos, 0, copy.pos, 0, pos.length );
+ copy.neighbors.makeCopyOf( neighbors );
+ return copy;
+ }
+
+ static class Neighbors
+ {
+ double[] distances;
+ int[] indices;
+ int size;
+
+ Neighbors()
+ {
+ final int capacity = 10;
+ distances = new double[ capacity ];
+ indices = new int[ capacity ];
+ }
+
+ void clear()
+ {
+ size = 0;
+ }
+
+ void add( final double distance, final int index )
+ {
+ if ( distances.length <= size )
+ {
+ // reallocate
+ final int newLength = distances.length * 2;
+ distances = Arrays.copyOf( distances, newLength );
+ indices = Arrays.copyOf( indices, newLength );
+ }
+ distances[ size ] = distance;
+ indices[ size ] = index;
+ ++size;
+ }
+
+ void sort()
+ {
+ final int[] order = new int[ size ];
+ Arrays.setAll( order, i -> i );
+ quicksort( 0, size - 1, distances, order );
+ System.arraycopy( reorder( distances, order ), 0, distances, 0, size );
+ System.arraycopy( reorder( indices, order ), 0, indices, 0, size );
+ }
+
+ void makeCopyOf( final Neighbors other )
+ {
+ distances = other.distances.clone();
+ indices = other.indices.clone();
+ size = other.size;
+ }
+ }
+}
diff --git a/src/main/java/net/imglib2/neighborsearch/KNearestNeighborSearch.java b/src/main/java/net/imglib2/neighborsearch/KNearestNeighborSearch.java
index 781253fb6..edb3f6c8e 100644
--- a/src/main/java/net/imglib2/neighborsearch/KNearestNeighborSearch.java
+++ b/src/main/java/net/imglib2/neighborsearch/KNearestNeighborSearch.java
@@ -89,7 +89,10 @@ public interface KNearestNeighborSearch< T > extends NearestNeighborSearch< T >
* the last search and the ith nearest neighbor, ordered
* by square Euclidean distance.
*/
- double getDistance( int i );
+ default double getDistance( final int i )
+ {
+ return Math.sqrt( getSquareDistance( i ) );
+ }
/**
* Create a copy.
diff --git a/src/main/java/net/imglib2/neighborsearch/KNearestNeighborSearchOnKDTree.java b/src/main/java/net/imglib2/neighborsearch/KNearestNeighborSearchOnKDTree.java
index 621fa6c79..4dcc426d2 100644
--- a/src/main/java/net/imglib2/neighborsearch/KNearestNeighborSearchOnKDTree.java
+++ b/src/main/java/net/imglib2/neighborsearch/KNearestNeighborSearchOnKDTree.java
@@ -34,10 +34,12 @@
package net.imglib2.neighborsearch;
-import net.imglib2.KDTree;
-import net.imglib2.KDTreeNode;
+import java.util.Arrays;
import net.imglib2.RealLocalizable;
import net.imglib2.Sampler;
+import net.imglib2.KDTree;
+import net.imglib2.KDTreeNode;
+import net.imglib2.kdtree.KNearestNeighborSearchImpl;
/**
* Implementation of {@link KNearestNeighborSearch} search for kd-trees.
@@ -46,35 +48,38 @@
*/
public class KNearestNeighborSearchOnKDTree< T > implements KNearestNeighborSearch< T >
{
- protected KDTree< T > tree;
-
- protected final int n;
+ private final KDTree< T > tree;
- protected final double[] pos;
+ private final int k;
- protected final int k;
+ private final KNearestNeighborSearchImpl impl;
- protected KDTreeNode< T >[] bestPoints;
-
- protected double[] bestSquDistances;
+ private final KDTreeNode< T >[] matches;
@SuppressWarnings( "unchecked" )
public KNearestNeighborSearchOnKDTree( final KDTree< T > tree, final int k )
{
this.tree = tree;
- this.n = tree.numDimensions();
- this.pos = new double[ n ];
this.k = k;
- this.bestPoints = new KDTreeNode[ k ];
- this.bestSquDistances = new double[ k ];
- for ( int i = 0; i < k; ++i )
- bestSquDistances[ i ] = Double.MAX_VALUE;
+ impl = new KNearestNeighborSearchImpl( tree.impl(), k );
+ matches = new KDTreeNode[ k ];
+ Arrays.setAll( matches, i -> tree.createNode() );
+ }
+
+ @SuppressWarnings( "unchecked" )
+ private KNearestNeighborSearchOnKDTree( final KNearestNeighborSearchOnKDTree< T > knn )
+ {
+ tree = knn.tree;
+ k = knn.k;
+ impl = knn.impl.copy();
+ matches = new KDTreeNode[ k ];
+ Arrays.setAll( matches, i -> tree.createNode().setNodeIndex( impl.bestIndex( i ) ) );
}
@Override
public int numDimensions()
{
- return n;
+ return tree.numDimensions();
}
@Override
@@ -84,67 +89,35 @@ public int getK()
}
@Override
- public void search( final RealLocalizable reference )
+ public void search( final RealLocalizable p )
{
- reference.localize( pos );
- for ( int i = 0; i < k; ++i )
- bestSquDistances[ i ] = Double.MAX_VALUE;
- searchNode( tree.getRoot() );
- }
-
- protected void searchNode( final KDTreeNode< T > current )
- {
- // consider the current node
- final double squDistance = current.squDistanceTo( pos );
- if ( squDistance < bestSquDistances[ k - 1 ] )
- {
- int i = k - 1;
- for ( int j = i - 1; i > 0 && squDistance < bestSquDistances[ j ]; --i, --j )
- {
- bestSquDistances[ i ] = bestSquDistances[ j ];
- bestPoints[ i ] = bestPoints[ j ];
- }
- bestSquDistances[ i ] = squDistance;
- bestPoints[ i ] = current;
- }
-
- final double axisDiff = pos[ current.getSplitDimension() ] - current.getSplitCoordinate();
- final double axisSquDistance = axisDiff * axisDiff;
- final boolean leftIsNearBranch = axisDiff < 0;
-
- // search the near branch
- final KDTreeNode< T > nearChild = leftIsNearBranch ? current.left : current.right;
- final KDTreeNode< T > awayChild = leftIsNearBranch ? current.right : current.left;
- if ( nearChild != null )
- searchNode( nearChild );
-
- // search the away branch - maybe
- if ( ( axisSquDistance <= bestSquDistances[ k - 1 ] ) && ( awayChild != null ) )
- searchNode( awayChild );
+ impl.search( p );
+ for ( int i = 0; i < k; i++ )
+ matches[ i ].setNodeIndex( impl.bestIndex( i ) );
}
@Override
public Sampler< T > getSampler( final int i )
{
- return bestPoints[ i ];
+ return matches[ i ];
}
@Override
public RealLocalizable getPosition( final int i )
{
- return bestPoints[ i ];
+ return matches[ i ];
}
@Override
public double getSquareDistance( final int i )
{
- return bestSquDistances[ i ];
+ return impl.bestSquDistance( i );
}
@Override
- public double getDistance( final int i )
+ public KNearestNeighborSearchOnKDTree< T > copy()
{
- return Math.sqrt( bestSquDistances[ i ] );
+ return new KNearestNeighborSearchOnKDTree<>( this );
}
/* NearestNeighborSearch */
@@ -172,17 +145,4 @@ public double getDistance()
{
return getDistance( 0 );
}
-
- @Override
- public KNearestNeighborSearchOnKDTree< T > copy()
- {
- final KNearestNeighborSearchOnKDTree< T > copy = new KNearestNeighborSearchOnKDTree< T >( tree, k );
- System.arraycopy( pos, 0, copy.pos, 0, pos.length );
- for ( int i = 0; i < k; ++i )
- {
- copy.bestPoints[ i ] = bestPoints[ i ];
- copy.bestSquDistances[ i ] = bestSquDistances[ i ];
- }
- return copy;
- }
}
diff --git a/src/main/java/net/imglib2/neighborsearch/NearestNeighborSearch.java b/src/main/java/net/imglib2/neighborsearch/NearestNeighborSearch.java
index 43d02daea..e2888df56 100644
--- a/src/main/java/net/imglib2/neighborsearch/NearestNeighborSearch.java
+++ b/src/main/java/net/imglib2/neighborsearch/NearestNeighborSearch.java
@@ -81,7 +81,10 @@ public interface NearestNeighborSearch< T > extends EuclideanSpace
* the last search and the nearest neighbor, ordered by square Euclidean
* distance.
*/
- double getDistance();
+ default double getDistance()
+ {
+ return Math.sqrt( getSquareDistance() );
+ }
/**
* Create a copy.
diff --git a/src/main/java/net/imglib2/neighborsearch/NearestNeighborSearchOnKDTree.java b/src/main/java/net/imglib2/neighborsearch/NearestNeighborSearchOnKDTree.java
index ce8980b1e..d94514d5b 100644
--- a/src/main/java/net/imglib2/neighborsearch/NearestNeighborSearchOnKDTree.java
+++ b/src/main/java/net/imglib2/neighborsearch/NearestNeighborSearchOnKDTree.java
@@ -34,10 +34,11 @@
package net.imglib2.neighborsearch;
-import net.imglib2.KDTree;
-import net.imglib2.KDTreeNode;
import net.imglib2.RealLocalizable;
import net.imglib2.Sampler;
+import net.imglib2.KDTree;
+import net.imglib2.KDTreeNode;
+import net.imglib2.kdtree.NearestNeighborSearchImpl;
/**
* Implementation of {@link NearestNeighborSearch} search for kd-trees.
@@ -47,60 +48,38 @@
*/
public class NearestNeighborSearchOnKDTree< T > implements NearestNeighborSearch< T >
{
- protected KDTree< T > tree;
-
- protected final int n;
+ private final KDTree< T > tree;
- protected final double[] pos;
+ private final NearestNeighborSearchImpl impl;
- protected KDTreeNode< T > bestPoint;
-
- protected double bestSquDistance;
+ private final KDTreeNode< T > bestPoint;
public NearestNeighborSearchOnKDTree( final KDTree< T > tree )
{
- n = tree.numDimensions();
- pos = new double[ n ];
this.tree = tree;
+ impl = new NearestNeighborSearchImpl( tree.impl() );
+ bestPoint = tree.createNode();
}
- @Override
- public int numDimensions()
+ private NearestNeighborSearchOnKDTree( final NearestNeighborSearchOnKDTree< T > nn )
{
- return n;
+ tree = nn.tree;
+ impl = nn.impl.copy();
+ bestPoint = tree.createNode();
+ bestPoint.setNodeIndex( nn.impl.bestIndex() );
}
@Override
- public void search( final RealLocalizable p )
+ public int numDimensions()
{
- p.localize( pos );
- bestSquDistance = Double.MAX_VALUE;
- searchNode( tree.getRoot() );
+ return tree.numDimensions();
}
- protected void searchNode( final KDTreeNode< T > current )
+ @Override
+ public void search( final RealLocalizable p )
{
- // consider the current node
- final double distance = current.squDistanceTo( pos );
- if ( distance < bestSquDistance )
- {
- bestSquDistance = distance;
- bestPoint = current;
- }
-
- final double axisDiff = pos[ current.getSplitDimension() ] - current.getSplitCoordinate();
- final double axisSquDistance = axisDiff * axisDiff;
- final boolean leftIsNearBranch = axisDiff < 0;
-
- // search the near branch
- final KDTreeNode< T > nearChild = leftIsNearBranch ? current.left : current.right;
- final KDTreeNode< T > awayChild = leftIsNearBranch ? current.right : current.left;
- if ( nearChild != null )
- searchNode( nearChild );
-
- // search the away branch - maybe
- if ( ( axisSquDistance <= bestSquDistance ) && ( awayChild != null ) )
- searchNode( awayChild );
+ impl.search( p );
+ bestPoint.setNodeIndex( impl.bestIndex() );
}
@Override
@@ -118,22 +97,12 @@ public RealLocalizable getPosition()
@Override
public double getSquareDistance()
{
- return bestSquDistance;
- }
-
- @Override
- public double getDistance()
- {
- return Math.sqrt( bestSquDistance );
+ return impl.bestSquDistance();
}
@Override
public NearestNeighborSearchOnKDTree< T > copy()
{
- final NearestNeighborSearchOnKDTree< T > copy = new NearestNeighborSearchOnKDTree< T >( tree );
- System.arraycopy( pos, 0, copy.pos, 0, pos.length );
- copy.bestPoint = bestPoint;
- copy.bestSquDistance = bestSquDistance;
- return copy;
+ return new NearestNeighborSearchOnKDTree<>( this );
}
}
diff --git a/src/main/java/net/imglib2/neighborsearch/RadiusNeighborSearch.java b/src/main/java/net/imglib2/neighborsearch/RadiusNeighborSearch.java
index d55689753..02aa94df8 100644
--- a/src/main/java/net/imglib2/neighborsearch/RadiusNeighborSearch.java
+++ b/src/main/java/net/imglib2/neighborsearch/RadiusNeighborSearch.java
@@ -106,5 +106,13 @@ public interface RadiusNeighborSearch< T > extends EuclideanSpace
* Access the Euclidean distance between the reference location as used for
* the last search and the ith neighbor.
*/
- double getDistance( int i );
+ default double getDistance( final int i )
+ {
+ return Math.sqrt( getSquareDistance( i ) );
+ }
+
+ /**
+ * Create a copy.
+ */
+ RadiusNeighborSearch< T > copy();
}
diff --git a/src/main/java/net/imglib2/neighborsearch/RadiusNeighborSearchOnKDTree.java b/src/main/java/net/imglib2/neighborsearch/RadiusNeighborSearchOnKDTree.java
index 28e00b450..4fd670808 100644
--- a/src/main/java/net/imglib2/neighborsearch/RadiusNeighborSearchOnKDTree.java
+++ b/src/main/java/net/imglib2/neighborsearch/RadiusNeighborSearchOnKDTree.java
@@ -35,115 +35,90 @@
package net.imglib2.neighborsearch;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-
-import net.imglib2.KDTree;
-import net.imglib2.KDTreeNode;
+import java.util.List;
import net.imglib2.RealLocalizable;
import net.imglib2.Sampler;
-import net.imglib2.util.ValuePair;
+import net.imglib2.KDTree;
+import net.imglib2.KDTreeNode;
+import net.imglib2.kdtree.RadiusNeighborSearchImpl;
/**
* Implementation of {@link RadiusNeighborSearch} search for kd-trees.
- *
+ *
* @author Tobias Pietzsch
*/
public class RadiusNeighborSearchOnKDTree< T > implements RadiusNeighborSearch< T >
{
- protected KDTree< T > tree;
-
- protected final int n;
+ private final KDTree< T > tree;
- protected final double[] pos;
+ private final RadiusNeighborSearchImpl impl;
- protected ArrayList< ValuePair< KDTreeNode< T >, Double > > resultPoints;
+ private final List< KDTreeNode< T > > matches;
public RadiusNeighborSearchOnKDTree( final KDTree< T > tree )
{
this.tree = tree;
- this.n = tree.numDimensions();
- this.pos = new double[ n ];
- this.resultPoints = new ArrayList< ValuePair< KDTreeNode< T >, Double > >();
+ impl = new RadiusNeighborSearchImpl( tree.impl() );
+ matches = new ArrayList<>();
}
- @Override
- public void search( final RealLocalizable reference, final double radius, final boolean sortResults )
+ private RadiusNeighborSearchOnKDTree( final RadiusNeighborSearchOnKDTree< T > other )
{
- assert radius >= 0;
- reference.localize( pos );
- resultPoints.clear();
- searchNode( tree.getRoot(), radius * radius );
- if ( sortResults )
- {
- Collections.sort( resultPoints, new Comparator< ValuePair< KDTreeNode< T >, Double > >()
- {
- @Override
- public int compare( final ValuePair< KDTreeNode< T >, Double > o1, final ValuePair< KDTreeNode< T >, Double > o2 )
- {
- return Double.compare( o1.b, o2.b );
- }
- } );
- }
+ tree = other.tree;
+ impl = other.impl.copy();
+ matches = new ArrayList<>();
+ for ( final KDTreeNode< T > match : other.matches )
+ matches.add( tree.createNode().setNodeIndex( match.nodeIndex() ) );
}
@Override
- public int numDimensions()
+ public void search( final RealLocalizable reference, final double radius, final boolean sortResults )
{
- return n;
+ impl.search( reference, radius, sortResults );
+ fillMatches( 0, impl.numNeighbors() - 1 );
}
- protected void searchNode( final KDTreeNode< T > current, final double squRadius )
+ private void fillMatches( final int first, final int last )
{
- // consider the current node
- final double squDistance = current.squDistanceTo( pos );
- if ( squDistance <= squRadius )
- {
- resultPoints.add( new ValuePair< KDTreeNode< T >, Double >( current, squDistance ) );
- }
-
- final double axisDiff = pos[ current.getSplitDimension() ] - current.getSplitCoordinate();
- final double axisSquDistance = axisDiff * axisDiff;
- final boolean leftIsNearBranch = axisDiff < 0;
-
- // search the near branch
- final KDTreeNode< T > nearChild = leftIsNearBranch ? current.left : current.right;
- final KDTreeNode< T > awayChild = leftIsNearBranch ? current.right : current.left;
- if ( nearChild != null )
- searchNode( nearChild, squRadius );
+ while ( matches.size() < last + 1 )
+ matches.add( tree.createNode() );
+ for ( int i = 0; i <= last; ++i )
+ matches.get( i ).setNodeIndex( impl.bestIndex( i ) );
+ }
- // search the away branch - maybe
- if ( ( axisSquDistance <= squRadius ) && ( awayChild != null ) )
- searchNode( awayChild, squRadius );
+ @Override
+ public int numDimensions()
+ {
+ return tree.numDimensions();
}
@Override
public int numNeighbors()
{
- return resultPoints.size();
+ return impl.numNeighbors();
}
@Override
public Sampler< T > getSampler( final int i )
{
- return resultPoints.get( i ).a;
+ return matches.get( i );
}
@Override
public RealLocalizable getPosition( final int i )
{
- return resultPoints.get( i ).a;
+ return matches.get( i );
}
@Override
public double getSquareDistance( final int i )
{
- return resultPoints.get( i ).b;
+ return impl.bestSquDistance( i );
}
@Override
- public double getDistance( final int i )
+ public RadiusNeighborSearchOnKDTree< T > copy()
{
- return Math.sqrt( resultPoints.get( i ).b );
+ return new RadiusNeighborSearchOnKDTree<>( this );
}
}
diff --git a/src/test/java/net/imglib2/kdtree/KDTreeBenchmark.java b/src/test/java/net/imglib2/kdtree/KDTreeBenchmark.java
new file mode 100644
index 000000000..d7eef7c88
--- /dev/null
+++ b/src/test/java/net/imglib2/kdtree/KDTreeBenchmark.java
@@ -0,0 +1,186 @@
+package net.imglib2.kdtree;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import net.imglib2.KDTree;
+import net.imglib2.RealLocalizable;
+import net.imglib2.RealPoint;
+import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree;
+import net.imglib2.neighborsearch.KNearestNeighborSearchOnKDTree;
+import net.imglib2.neighborsearch.RadiusNeighborSearchOnKDTree;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openjdk.jmh.runner.options.TimeValue;
+import org.openjdk.jmh.annotations.Setup;
+
+@State( Scope.Benchmark )
+public class KDTreeBenchmark
+{
+// @Param({"3"})
+// public int n;
+//
+// @Param({"10000", "100000", "1000000"})
+// public int numDataVertices;
+//
+// @Param({"1000"})
+// public int numTestVertices;
+//
+ public int n = 3;
+ public int k = 10;
+ public int radius = 1;
+ public int numDataVertices = 100000;
+ public int numTestVertices = 1000;
+ public double minCoordinateValue = -5;
+ public double maxCoordinateValue = 5;
+
+ List< RealPoint > dataVertices;
+ List< RealPoint > testVertices;
+
+ private KDTree< RealPoint > kdtree;
+
+ @Setup
+ public void setup()
+ {
+ createVertices();
+// createVerticesSeqTest();
+ kdtree = new KDTree<>( dataVertices, dataVertices );
+// spoil();
+ }
+
+ public void spoil() {
+ final double[][] points = KDTreeUtils.initPositions( n, numDataVertices, dataVertices );
+ final int[] tree = KDTreeUtils.makeTree( points );
+ final double[][] treePoints = KDTreeUtils.reorder( points, tree );
+ final KDTreeImpl impl = new KDTreeImpl(new KDTreePositions.Nested(treePoints));
+ final NearestNeighborSearchImpl search = new NearestNeighborSearchImpl( impl );
+ for ( RealPoint testVertex : testVertices )
+ search.search( testVertex );
+ }
+
+ @Benchmark
+ @BenchmarkMode( Mode.AverageTime )
+ @OutputTimeUnit( TimeUnit.MILLISECONDS )
+ public void createKDTree()
+ {
+ new KDTree<>( dataVertices, dataVertices );
+ }
+
+ @Benchmark
+ @BenchmarkMode( Mode.AverageTime )
+ @OutputTimeUnit( TimeUnit.MILLISECONDS )
+ public void nearestNeighborSearch()
+ {
+ final NearestNeighborSearchOnKDTree< RealPoint > kd = new NearestNeighborSearchOnKDTree<>( kdtree );
+ for ( final RealLocalizable t : testVertices )
+ {
+ kd.search( t );
+ kd.getSampler().get();
+ }
+ }
+
+ @Benchmark
+ @BenchmarkMode( Mode.AverageTime )
+ @OutputTimeUnit( TimeUnit.MILLISECONDS )
+ public void kNearestNeighborSearch()
+ {
+ final KNearestNeighborSearchOnKDTree< RealPoint > kd = new KNearestNeighborSearchOnKDTree<>( kdtree, k );
+ for ( final RealLocalizable t : testVertices )
+ {
+ kd.search( t );
+ kd.getSampler().get();
+// for ( int i = 0; i < k; i++ )
+// {
+// kd.getSampler( i ).get();
+// }
+ }
+ }
+
+ @Benchmark
+ @BenchmarkMode( Mode.AverageTime )
+ @OutputTimeUnit( TimeUnit.MILLISECONDS )
+ public void radiusNeighborSearch()
+ {
+ final RadiusNeighborSearchOnKDTree< RealPoint > kd = new RadiusNeighborSearchOnKDTree<>( kdtree );
+ for ( final RealLocalizable t : testVertices )
+ {
+ kd.search( t, radius, true );
+ for ( int i = 0; i < Math.min( kd.numNeighbors(), k ); i++ )
+ {
+ kd.getSampler( i ).get();
+ }
+ }
+ }
+
+ private void createVertices()
+ {
+ final double[] p = new double[ n ];
+ final double size = ( maxCoordinateValue - minCoordinateValue );
+ final Random rnd = new Random( 4379 );
+ dataVertices = new ArrayList<>();
+ for ( int i = 0; i < numDataVertices; ++i )
+ {
+ for ( int d = 0; d < n; ++d )
+ p[ d ] = rnd.nextDouble() * size + minCoordinateValue;
+ dataVertices.add( new RealPoint( p ) );
+ }
+ testVertices = new ArrayList<>();
+ for ( int i = 0; i < numTestVertices; ++i )
+ {
+ for ( int d = 0; d < n; ++d )
+ p[ d ] = rnd.nextDouble() * 2 * size + minCoordinateValue - size / 2;
+ testVertices.add( new RealPoint( p ) );
+ }
+ }
+
+ private void createVerticesSeqTest()
+ {
+ final double[] p = new double[ n ];
+ final double size = ( maxCoordinateValue - minCoordinateValue );
+ final Random rnd = new Random( 4379 );
+ dataVertices = new ArrayList<>();
+ for ( int i = 0; i < numDataVertices; ++i )
+ {
+ for ( int d = 0; d < n; ++d )
+ p[ d ] = rnd.nextDouble() * size + minCoordinateValue;
+ dataVertices.add( new RealPoint( p ) );
+ }
+ testVertices = new ArrayList<>();
+ for ( int i = 0; i < numTestVertices; ++i )
+ {
+ if ( rnd.nextDouble() < 0.8 )
+ {
+ int d = rnd.nextInt( n );
+ p[ d ] += size / 10000.0;
+ }
+ else
+ {
+ for ( int d = 0; d < n; ++d )
+ p[ d ] = rnd.nextDouble() * 2 * size + minCoordinateValue - size / 2;
+ }
+ testVertices.add( new RealPoint( p ) );
+ }
+ }
+
+ public static void main( final String... args ) throws RunnerException
+ {
+ final Options opt = new OptionsBuilder()
+ .include( KDTreeBenchmark.class.getSimpleName() )
+ .forks( 0 )
+ .warmupIterations( 4 )
+ .measurementIterations( 8 )
+ .warmupTime( TimeValue.milliseconds( 500 ) )
+ .measurementTime( TimeValue.milliseconds( 500 ) )
+ .build();
+ new Runner( opt ).run();
+ }
+}
diff --git a/src/test/java/net/imglib2/kdtree/KDTreeImplTest.java b/src/test/java/net/imglib2/kdtree/KDTreeImplTest.java
new file mode 100644
index 000000000..e683320a8
--- /dev/null
+++ b/src/test/java/net/imglib2/kdtree/KDTreeImplTest.java
@@ -0,0 +1,151 @@
+package net.imglib2.kdtree;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import net.imglib2.RealLocalizable;
+import net.imglib2.RealPoint;
+import net.imglib2.util.LinAlgHelpers;
+import org.junit.Assert;
+import org.junit.Before;
+
+import java.util.ArrayList;
+import java.util.Random;
+import org.junit.Test;
+
+public class KDTreeImplTest {
+
+ public int n = 3;
+ public int numDataVertices = 100;
+ public int numTestVertices = 10;
+ public double minCoordinateValue = -5;
+ public double maxCoordinateValue = 5;
+
+ public List< RealPoint > dataVertices;
+ public List< RealPoint > testVertices;
+
+ @Before
+ public void init()
+ {
+ final double[] p = new double[ n ];
+ final double size = ( maxCoordinateValue - minCoordinateValue );
+ final Random rnd = new Random( 4379 );
+ dataVertices = new ArrayList<>();
+ for ( int i = 0; i < numDataVertices; ++i )
+ {
+ for ( int d = 0; d < n; ++d )
+ p[ d ] = rnd.nextDouble() * size + minCoordinateValue;
+ dataVertices.add( new RealPoint( p ) );
+ }
+ testVertices = new ArrayList<>();
+ for ( int i = 0; i < numTestVertices; ++i )
+ {
+ for ( int d = 0; d < n; ++d )
+ p[ d ] = rnd.nextDouble() * 2 * size + minCoordinateValue - size / 2;
+ testVertices.add( new RealPoint( p ) );
+ }
+ }
+
+ @Test
+ public void testNearestNeighborSearch()
+ {
+ final double[][] points = KDTreeUtils.initPositions( n, numDataVertices, dataVertices );
+ final int[] tree = KDTreeUtils.makeTree( points );
+ final double[][] treePoints = KDTreeUtils.reorder( points, tree );
+ final KDTreeImpl impl = new KDTreeImpl(new KDTreePositions.Nested(treePoints));
+ final NearestNeighborSearchImpl search = new NearestNeighborSearchImpl( impl );
+
+ for ( RealPoint testVertex : testVertices )
+ {
+ final int expected = findNearestNeighborExhaustive( testVertex );
+
+ search.search( testVertex );
+ final int actual = tree[ search.bestIndex() ];
+
+// System.out.println( "actual = " + actual + ", expected = " + expected );
+ Assert.assertEquals( expected, actual );
+ }
+ }
+
+ @Test
+ public void testKNearestNeighborSearch()
+ {
+ final int k = 10;
+
+ final double[][] points = KDTreeUtils.initPositions( n, numDataVertices, dataVertices );
+ final int[] tree = KDTreeUtils.makeTree( points );
+ final double[][] treePoints = KDTreeUtils.reorder( points, tree );
+ final KDTreeImpl impl = new KDTreeImpl(new KDTreePositions.Nested(treePoints));
+ final KNearestNeighborSearchImpl search = new KNearestNeighborSearchImpl( impl, k );
+
+ for ( RealPoint testVertex : testVertices )
+ {
+ final int[] expecteds = findNearestNeighborsExhaustive( testVertex, k );
+
+ search.search( testVertex );
+ final int[] actuals = new int[ k ];
+ Arrays.setAll( actuals, i -> tree[ search.bestIndex( i ) ] );
+
+// System.out.println( "actual = " + actual + ", expected = " + expected );
+ Assert.assertArrayEquals( expecteds, actuals );
+ }
+ }
+
+ @Test
+ public void testRadiusNeighborSearch()
+ {
+ final double radius = 7;
+
+ final double[][] points = KDTreeUtils.initPositions( n, numDataVertices, dataVertices );
+ final int[] tree = KDTreeUtils.makeTree( points );
+ final double[][] treePoints = KDTreeUtils.reorder( points, tree );
+ final KDTreeImpl impl = new KDTreeImpl(new KDTreePositions.Nested(treePoints));
+ final RadiusNeighborSearchImpl search = new RadiusNeighborSearchImpl( impl );
+
+ for ( RealPoint testVertex : testVertices )
+ {
+ final int[] expecteds = findRadiusNeighborsExhaustive( testVertex, radius );
+
+ search.search( testVertex, radius, true );
+ final int[] actuals = new int[ search.numNeighbors() ];
+ Arrays.setAll( actuals, i -> tree[ search.bestIndex( i ) ] );
+
+ Assert.assertArrayEquals( expecteds, actuals );
+ }
+ }
+
+ private int findNearestNeighborExhaustive( final RealLocalizable point )
+ {
+ return findNearestNeighborsExhaustive( point, 1 )[ 0 ];
+ }
+
+ private int[] findNearestNeighborsExhaustive( final RealLocalizable point, final int k )
+ {
+ final List< RealPoint > sorted = new ArrayList<>();
+ sorted.addAll( dataVertices );
+ sorted.sort( Comparator.comparing( p -> distance( point, p ) ) );
+ final int[] neighbors = new int[ k ];
+ Arrays.setAll(neighbors, i -> dataVertices.indexOf( sorted.get( i ) ) );
+ return neighbors;
+ }
+
+ private static double distance( final RealLocalizable p1, final RealLocalizable p2 )
+ {
+ return LinAlgHelpers.distance( p2.positionAsDoubleArray(), p1.positionAsDoubleArray() );
+ }
+
+ private int[] findRadiusNeighborsExhaustive( final RealLocalizable point, final double radius )
+ {
+ final List< RealPoint > sorted = new ArrayList<>();
+ dataVertices.forEach( p -> {
+ if ( distance( point, p ) <= radius )
+ {
+ sorted.add( p );
+ }
+ } );
+ sorted.sort( Comparator.comparing( p -> distance( point, p ) ) );
+ final int[] neighbors = new int[ sorted.size() ];
+ Arrays.setAll(neighbors, i -> dataVertices.indexOf( sorted.get( i ) ) );
+ return neighbors;
+ }
+}
diff --git a/src/test/java/net/imglib2/nearestneighbor/KDTreeTest.java b/src/test/java/net/imglib2/neighborsearch/KDTreeTest.java
similarity index 93%
rename from src/test/java/net/imglib2/nearestneighbor/KDTreeTest.java
rename to src/test/java/net/imglib2/neighborsearch/KDTreeTest.java
index 5ff8cb5a5..0d9fb8d08 100644
--- a/src/test/java/net/imglib2/nearestneighbor/KDTreeTest.java
+++ b/src/test/java/net/imglib2/neighborsearch/KDTreeTest.java
@@ -32,7 +32,7 @@
* #L%
*/
-package net.imglib2.nearestneighbor;
+package net.imglib2.neighborsearch;
import static org.junit.Assert.assertTrue;
@@ -58,7 +58,7 @@ public class KDTreeTest
{
protected static boolean testNearestNeighbor( final int numDimensions, final int numPoints, final int numTests, final float min, final float max )
{
- final ArrayList< RealPoint > points = new ArrayList< RealPoint >();
+ final ArrayList< RealPoint > points = new ArrayList<>();
final Random rnd = new Random( 435435435 );
final float[] p = new float[ numDimensions ];
@@ -75,13 +75,13 @@ protected static boolean testNearestNeighbor( final int numDimensions, final int
}
long start = System.currentTimeMillis();
- final KDTree< RealPoint > kdTree = new KDTree< RealPoint >( points, points );
- final NearestNeighborSearchOnKDTree< RealPoint > kd = new NearestNeighborSearchOnKDTree< RealPoint >( kdTree );
+ final KDTree< RealPoint > kdTree = new KDTree<>( points, points );
+ final NearestNeighborSearchOnKDTree< RealPoint > kd = new NearestNeighborSearchOnKDTree<>( kdTree );
final long kdSetupTime = System.currentTimeMillis() - start;
System.out.println( "kdtree setup took: " + ( kdSetupTime ) + " ms." );
start = System.currentTimeMillis();
- final ArrayList< RealPoint > testpoints = new ArrayList< RealPoint >();
+ final ArrayList< RealPoint > testpoints = new ArrayList<>();
for ( int i = 0; i < numTests; ++i )
{
for ( int d = 0; d < numDimensions; ++d )
@@ -163,7 +163,7 @@ private static RealPoint findNearestNeighborExhaustive( final ArrayList< RealPoi
protected static boolean testKNearestNeighbor( final int neighbors, final int numDimensions, final int numPoints, final int numTests, final float min, final float max )
{
- final ArrayList< RealPoint > points = new ArrayList< RealPoint >();
+ final ArrayList< RealPoint > points = new ArrayList<>();
final Random rnd = new Random( 435435435 );
final float[] p = new float[ numDimensions ];
@@ -180,13 +180,13 @@ protected static boolean testKNearestNeighbor( final int neighbors, final int nu
}
long start = System.currentTimeMillis();
- final KDTree< RealPoint > kdTree = new KDTree< RealPoint >( points, points );
- final KNearestNeighborSearchOnKDTree< RealPoint > kd = new KNearestNeighborSearchOnKDTree< RealPoint >( kdTree, neighbors );
+ final KDTree< RealPoint > kdTree = new KDTree<>( points, points );
+ final KNearestNeighborSearchOnKDTree< RealPoint > kd = new KNearestNeighborSearchOnKDTree<>( kdTree, neighbors );
final long kdSetupTime = System.currentTimeMillis() - start;
System.out.println( "kdtree setup took: " + ( kdSetupTime ) + " ms." );
start = System.currentTimeMillis();
- final ArrayList< RealPoint > testpoints = new ArrayList< RealPoint >();
+ final ArrayList< RealPoint > testpoints = new ArrayList<>();
for ( int i = 0; i < numTests; ++i )
{
for ( int d = 0; d < numDimensions; ++d )
@@ -287,7 +287,7 @@ private static RealPoint[] findKNearestNeighborExhaustive( final ArrayList< Real
protected static boolean testRadiusNeighbor( final int numDimensions, final int numPoints, final int numTests, final float min, final float max )
{
- final ArrayList< RealPoint > points = new ArrayList< RealPoint >();
+ final ArrayList< RealPoint > points = new ArrayList<>();
final Random rnd = new Random( 435435435 );
final float[] p = new float[ numDimensions ];
@@ -306,13 +306,13 @@ protected static boolean testRadiusNeighbor( final int numDimensions, final int
final double radius = rnd.nextDouble() * size / 10;
long start = System.currentTimeMillis();
- final KDTree< RealPoint > kdTree = new KDTree< RealPoint >( points, points );
- final RadiusNeighborSearchOnKDTree< RealPoint > kd = new RadiusNeighborSearchOnKDTree< RealPoint >( kdTree );
+ final KDTree< RealPoint > kdTree = new KDTree<>( points, points );
+ final RadiusNeighborSearchOnKDTree< RealPoint > kd = new RadiusNeighborSearchOnKDTree<>( kdTree );
final long kdSetupTime = System.currentTimeMillis() - start;
System.out.println( "kdtree setup took: " + ( kdSetupTime ) + " ms." );
start = System.currentTimeMillis();
- final ArrayList< RealPoint > testpoints = new ArrayList< RealPoint >();
+ final ArrayList< RealPoint > testpoints = new ArrayList<>();
for ( int i = 0; i < numTests; ++i )
{
for ( int d = 0; d < numDimensions; ++d )
@@ -384,7 +384,7 @@ protected static boolean testRadiusNeighbor( final int numDimensions, final int
private static ArrayList< ValuePair< RealPoint, Double > > findNeighborsRadiusExhaustive( final ArrayList< RealPoint > points, final RealPoint t, final double radius, final boolean sortResults )
{
- final ArrayList< ValuePair< RealPoint, Double > > withinRadius = new ArrayList< ValuePair< RealPoint, Double > >();
+ final ArrayList< ValuePair< RealPoint, Double > > withinRadius = new ArrayList<>();
final int n = t.numDimensions();
final float[] tpos = new float[ n ];
@@ -400,7 +400,7 @@ private static ArrayList< ValuePair< RealPoint, Double > > findNeighborsRadiusEx
dist = Math.sqrt( dist );
if ( dist <= radius )
- withinRadius.add( new ValuePair< RealPoint, Double >( p, dist ) );
+ withinRadius.add( new ValuePair<>( p, dist ) );
}
if ( sortResults )
diff --git a/src/test/java/net/imglib2/nearestneighbor/NearestNeighborSearchOnIterableRealIntervalTest.java b/src/test/java/net/imglib2/neighborsearch/NearestNeighborSearchOnIterableRealIntervalTest.java
similarity index 99%
rename from src/test/java/net/imglib2/nearestneighbor/NearestNeighborSearchOnIterableRealIntervalTest.java
rename to src/test/java/net/imglib2/neighborsearch/NearestNeighborSearchOnIterableRealIntervalTest.java
index ab05d1444..9e82adca7 100644
--- a/src/test/java/net/imglib2/nearestneighbor/NearestNeighborSearchOnIterableRealIntervalTest.java
+++ b/src/test/java/net/imglib2/neighborsearch/NearestNeighborSearchOnIterableRealIntervalTest.java
@@ -32,7 +32,7 @@
* #L%
*/
-package net.imglib2.nearestneighbor;
+package net.imglib2.neighborsearch;
import static org.junit.Assert.assertTrue;
import net.imglib2.RealCursor;