/* jCAE stand for Java Computer Aided Engineering. Features are : Small CAD modeler, Finite element mesher, Plugin architecture. Copyright (C) 2004,2005, by EADS CRC Copyright (C) 2007,2008,2009,2010, by EADS France This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jcae.mesh.amibe.metrics; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.logging.Level; import java.util.logging.Logger; /** * Quadtree structure to store 2D vertices. When adjacent relations have not * yet been set, a quadtree is an efficient way to locate a point among a set * of points and triangles. * * <p> * Integer coordinates are used for two reasons: a better control on accuracy * of geometrical operations, and simpler operations on vertex location because * cells have power of two side length and bitwise operators can be used * instead of floating point operations. The downside is that the conversion * between double and integer coordinates must be known in advance, which is * why constructor needs a bounding box as argument. * </p> * * <p> * Each {@link Cell} contains either vertices or four children nodes * (some of them may be <code>null</code>). A cell can contain at most * {@link #BUCKETSIZE} vertices (default is 10). When this number * is exceeded, the cell is splitted and vertices are stored in these children. * On the contrary, when all vertices are removed from a cell, it is deleted. * And when all children of a cell are null, this cell is removed. * </p> * * <p> * Quadtree cells are very compact, they do not contain any locational * information. It is instead passed to the * {@link KdTreeProcedure#action(Object, int, int[])} method. * This design had been chosen for performance reasons on large meshes, and in * practice it works very well because quadtrees are used for vertex location, * and no neighbourhood information is needed. * </p> * * <p> * Quadtree traversal is performed by the {@link #walk(KdTreeProcedure)} method. * Here is an example to collect all vertices in a list: * </p> * <pre> * public final class collectAllVerticesProcedure implements KdTreeProcedure * { * public Collection<Vertex> vertexList = new ArrayList<Vertex>(); * public final int action(Object o, int s, int [] i0) * { * Cell self = (Cell) o; * if (self.nItems > 0) * { * for (int i = 0; i < self.nItems; i++) * vertexList.add((Vertex) self.subCell[i]); * } * return KdTreeProcedure.OK; * } * } * </pre> * <p> * This procedure is applied on all cells recursively in prefix order. If it * returns {@link KdTreeProcedure#ABORT}, {@link #walk(KdTreeProcedure)} aborts its * processing immediately; {@link KdTreeProcedure#OK} return value means that processing can * continue normally, and {@link KdTreeProcedure#SKIPCHILD} return value means that children * nodes are skipped. * </p> * * <p> * In Euclidian 2D space, vertices which have a distance to a point <code>p</code> * lower than <code>d</code> are contained in a circle centered at <code>p</code> * with radius <code>d</code>. With Riemannian metrics, this circle becomes * an ellipsis. This ellipsis is only determined by local properties of the * surface at point <code>p</code>. * If we already found a point <code>V1</code> at a distance <code>d1</code>, * vertices which belong to a quadtree cell not intersecting this ellipsis * do not need to be considered. * </p> * * <p> * Below is an algorithm to find the nearest vertex in the quadtree of a given * point <code>p</code>: * </p> * <ol type="1"> * <li>Initialization: <code>dmin=Double.MAX_VALUE</code>, <code>result=null</code></li> * <li>Traverse all quadtree cells. * <ol type="a"> * <li>If this cell does not intersect the ellipsis centered at * <code>p</code> of vertices at a distance lower than <code>dmin</code>, * then skip this cell and its children.</li> * <li>Otherwise, if this cell contains children nodes, do nothing so that * processaing continues normally on children nodes.</li> * <li>Otherwise, this cell contains vertices. For each vertex, compute * its distance to <code>p</code> and update <code>dmin</code> and * <code>result</code> if it is nearer than the current solution.</li> * </ol></li> * </ol> * * <p> * The implementation of {@link #getNearestVertex} has two * differences: * </p> * <ul> * <li>The starting point is computed by {@link #getNearVertex}. * This means that much more cells are skipped.</li> * <li>The ellipsis is replaced by a circle enclosing it, to have simpler * calculus. Using the real ellipsis could be tested though, it should * also speed up this processing.</li> * </ul> */ public class KdTree<T extends Location> { private static final Logger logger=Logger.getLogger(KdTree.class.getName()); /** * Cell of a {@link KdTree}. Each cell contains either four children nodes * or up to <code>BUCKETSIZE</code> vertices. When this number is exceeded, * the cell is splitted and vertices are moved to these children. * On the contrary, when all vertices are removed from a cell, it is deleted. * And when all children of a cell are null, this cell is removed. */ /** * Maximal number of vertices which can be stored in a cell. * This number must be at least 4, because children nodes are * stored in the same place as vertices, and a cell can have at * most 4 children. Its value is 10. */ private final int BUCKETSIZE; public class Cell { /** * Number of vertices stored below the current cell. If this cell * has children nodes, this value is negative and its opposite * value is the total number of vertices found in children nodes. * Otherwise, it contains the number of vertices which are stored * in the {@link #subCell} array. */ private int nItems; /** * References to bound objects. This variable either contains * four references to children nodes (some of which may be * <code>null</code>), or up to {@link KdTree#BUCKETSIZE} references * yo vertices. This compact storage is needed to reduce memory * usage. */ private Object [] subCell; public final boolean isLeaf() { return nItems >= 0; } public final int count() { if (nItems >= 0) return nItems; return -nItems; } @SuppressWarnings("unchecked") public final T getVertex(int i) { assert nItems > 0 && i < nItems; return (T) subCell[i]; } } private final int dimension; private final int nrSub; // Integer coordinates (like gridSize) must be long if MAXLEVEL > 30 private static final int MAXLEVEL = 30; private static final int gridSize = 1 << MAXLEVEL; private static final double DGridSize = gridSize; /** * Root of the kd-tree. */ private final Cell root; /** * Number of cells. */ public int nCells; /** * Conversion between double and integer coordinates. */ private final double [] x0; /** * Methods only available in 2D. */ public final K2DInterface k2D; /** * Dummy constructor. This instance must be properly initialised by calling * {@link #setup} before putting elements into it. * * @param d dimension (2 or 3) */ public KdTree(int d) { BUCKETSIZE = 10; dimension = d; if (d == 2) k2D = new K2D(); else k2D = new K2DInvalid(); nrSub = 1 << dimension; x0 = new double[dimension+1]; root = new Cell(); } /** * Create a new <code>KdTree</code> of the desired size. * * @param bbox coordinates of bottom-left vertex and upper-right vertices */ public KdTree(double [] bbox) { this(bbox, 10); } /** * Create a new <code>KdTree</code> of the desired size. * * @param bbox coordinates of bottom-left vertex and upper-right vertices * @param bucketsize bucket size */ KdTree(double [] bbox, int bucketsize) { BUCKETSIZE = bucketsize; dimension = bbox.length / 2; if (dimension == 2) k2D = new K2D(); else k2D = new K2DInvalid(); nrSub = 1 << dimension; x0 = new double[dimension+1]; root = new Cell(); setup(bbox); } /** * Computes {@link #x0} adapted to this bounding box. * * @param bbox coordinates of bottom-left vertex and upper-right vertices */ public final void setup(double [] bbox) { if (bbox.length != 2*dimension) throw new IllegalArgumentException(); if (nCells > 0) throw new RuntimeException("KdTree.setup() cannot be called after KdTree has been modified"); nCells = 1; double maxDelta = 0.0; for (int i = 0; i < dimension; i++) { x0[i] = bbox[i]; double delta = Math.abs(bbox[i+dimension] - bbox[i]); if (delta > maxDelta) maxDelta = delta; } maxDelta *= 1.01; x0[dimension] = DGridSize / maxDelta; if (logger.isLoggable(Level.FINE)) { StringBuilder sb = new StringBuilder("New KdTree int<--->double conversion vector; scale="); sb.append(x0[dimension]); sb.append(" origin=("); sb.append(x0[0]); for (int i = 1; i < dimension; i++) sb.append(",").append(x0[i]); sb.append(")\nBounding box:\n"); for (int i = 0; i < dimension; i++) { sb.append(bbox[i]); sb.append(" <= x["); sb.append(i); sb.append("] <= "); sb.append(bbox[i+dimension]); } logger.fine(sb.toString()); } } /** * Transform double coordinates into integer coordinates. * @param p double coordinates * @param i integer coordinates */ private void double2int(double [] p, int [] i) { for (int k = 0; k < dimension; k++) i[k] = (int) ((p[k] - x0[k]) * x0[dimension]); } private void double2int(T p, int[] i) { i[0] = (int) ((p.getX() - x0[0]) * x0[dimension]); i[1] = (int) ((p.getY() - x0[1]) * x0[dimension]); if(i.length > 2) i[2] = (int) ((p.getZ() - x0[2]) * x0[dimension]); } /** * Transform integer coordinates into double coordinates. * @param i integer coordinates * @param p double coordinates */ public final void int2double(int [] i, double [] p) { for (int k = 0; k < dimension; k++) p[k] = x0[k] + i[k] / x0[dimension]; } /** * Return the coordinates of the center of the grid. * @return the coordinates of the center of the grid. */ @SuppressWarnings("unused") private double [] center() { double [] p = new double[dimension]; for (int k = 0; k < dimension; k++) p[k] = x0[k] + DGridSize * 0.5 / x0[dimension]; return p; } /* * Return the index of the child node containing a given point. * A KdTree.Cell contains at most 4 children. Cell size is a power of * two, so locating a vertex can be performed by bitwise operators, as * shown below. * <pre> * ┌───┬───┐ * <>0 │ 2 │ 3 │ with * ├───┼───┤ I = i & size * J=0 │ 0 │ 1 │ J = j & size * └───┴───┘ * I=0 <>0 * </pre> * @param ijk coordinates of a vertex. * @param size cell size of children nodes. * @return the index of the child node containing this vertex. */ private int indexSubCell(int [] ijk, int size) { int ret = 0; if (size == 0) { double[] coords = new double[3]; int2double(ijk, coords); throw new RuntimeException( "Exceeded maximal number of levels for kd-trees around "+ Arrays.toString(coords) +"... Aborting"); } for (int k = 0; k < dimension; k++) { if ((ijk[k] & size) != 0) ret |= 1 << k; } return ret; } /** * Add a vertex to the kd-tree. * * @param v the vertex being added. * @return <code>true</code> if cell was full and had to be split, <code>false</code> otherwise. */ public final boolean add(T v) { if (nCells == 0) throw new RuntimeException("KdTree.setup() must be called before KdTree.add()"); boolean ret = false; Cell current = root; int s = gridSize; int [] ij = new int[dimension]; int [] oldij = new int[dimension]; double2int(v, ij); while (current.nItems < 0) { // nItems is negative means that this cell only // contains subcells, and its opposite is the // total number of nodes found in subcells. current.nItems--; s >>= 1; assert s > 0; int ind = indexSubCell(ij, s); if (null == current.subCell[ind]) { current.subCell[ind] = new Cell(); nCells++; } current = (Cell) current.subCell[ind]; } // If current box is full, split it into subcells while (current.nItems == BUCKETSIZE) { s >>= 1; assert s > 0; ret = true; Object[] newSubQuads = new Object[nrSub]; // Move points to their respective subcells. for (int i = 0; i < BUCKETSIZE; i++) { T p = current.getVertex(i); double2int(p, oldij); int ind = indexSubCell(oldij, s); Cell target = (Cell) newSubQuads[ind]; if (null == target) { target = new Cell(); newSubQuads[ind] = target; nCells++; target.subCell = new Object[BUCKETSIZE]; } target.subCell[target.nItems] = current.subCell[i]; target.nItems++; } current.subCell = newSubQuads; // current will point to another cell, adjust it now. current.nItems = - BUCKETSIZE - 1; int ind = indexSubCell(ij, s); if (null == current.subCell[ind]) { current.subCell[ind] = new Cell(); nCells++; } current = (Cell) current.subCell[ind]; } // Eventually insert the new point if (current.nItems == 0) current.subCell = new Object[BUCKETSIZE]; current.subCell[current.nItems] = v; current.nItems++; return ret; } /** * Remove a vertex from the kd-tree. * * @param v the vertex being removed. */ public final void remove(T v) { if (nCells == 0) throw new RuntimeException("KdTree.setup() must be called before KdTree.remove()"); Cell current = root; Cell last = root; Cell next; int lastPos = 0; int s = gridSize; int [] ij = new int[dimension]; double2int(v, ij); while (current.nItems < 0) { // nItems is negative current.nItems++; if (current.nItems == 0) last.subCell[lastPos] = null; s >>= 1; assert s > 0; int ind = indexSubCell(ij, s); next = (Cell) current.subCell[ind]; if (null == next) throw new RuntimeException("Vertex "+v+" is not present and can not be deleted"); last = current; lastPos = ind; current = next; } int offset = 0; for (int i = 0; i < current.nItems; i++) { if (v == current.subCell[i]) offset++; else if (offset > 0) current.subCell[i-offset] = current.subCell[i]; } if (offset == 0) throw new RuntimeException("Vertex "+v+" is not present and can not be deleted"); if (current.nItems > 1) { current.subCell[current.nItems-1] = null; current.nItems--; } else last.subCell[lastPos] = null; } private final class GetAllVerticesProcedure implements KdTreeProcedure { private final Collection<T> nodelist; private GetAllVerticesProcedure(int capacity) { nodelist = new ArrayList<T>(capacity); } public int action(Object o, int s, final int [] i0) { Cell self = (Cell) o; if (self.nItems > 0) { for (int i = 0; i < self.nItems; i++) nodelist.add(self.getVertex(i)); } return KdTreeProcedure.OK; } } /** * Return a collection of all vertices. * * @param capacity initial capacity of the <code>Collection</code>. * @return a collection containing all vertices. */ public final Collection<T> getAllVertices(int capacity) { GetAllVerticesProcedure gproc = new GetAllVerticesProcedure(capacity); walk(gproc); return gproc.nodelist; } /** * Perform an action on all cells in prefix order. * * The procedure is applied to the root cell, then recursively to * its children. If it returns <code>-1</code>, processing aborts * immediately and <code>false</code> is returned. If the * procedure returns <code>1</code>, cell children are not processed. * * @param proc procedure to apply on each cell. * @return <code>true</code> if all cells have been traversed, <code>false</code> otherwise. * @see KdTreeProcedure */ public final boolean walk(KdTreeProcedure proc) { int s = gridSize; int l = 0; int [] i0 = new int[dimension]; int [] posStack = new int[MAXLEVEL]; posStack[l] = 0; Object [] cellStack = new Object[MAXLEVEL]; cellStack[l] = root; while (true) { int res = proc.action(cellStack[l], s, i0); if (res == KdTreeProcedure.ABORT) return false; Cell current = (Cell) cellStack[l]; if (current.nItems < 0 && res == KdTreeProcedure.OK) { s >>= 1; assert s > 0; l++; assert l <= MAXLEVEL; for (int i = 0; i < nrSub; i++) { Object target = current.subCell[i]; if (null != target) { cellStack[l] = target; posStack[l] = i; break; } } for (int k = 0; k < dimension; k++) if ((posStack[l] & (1 << k)) != 0) i0[k] += s; } else { while (l > 0) { posStack[l]++; if (posStack[l] == nrSub) { for (int k = 0; k < dimension; k++) i0[k] -= s; s <<= 1; l--; } else { for (int k = 0; k < dimension; k++) { if ((posStack[l] & (1 << k)) != 0) { i0[k] += s; break; } i0[k] -= s; } if (null != ((Cell) cellStack[l-1]).subCell[posStack[l]]) break; } } if (l == 0) break; cellStack[l] = ((Cell) cellStack[l-1]).subCell[posStack[l]]; } } return true; } // Called in log messages private String coordinatesToString(T uv) { return "("+uv.getX()+", "+uv.getY()+", "+uv.getZ()+")"; } /** * Return a stored element of the <code>KdTree</code> which is * near from a given position. The algorithm is simplistic: the leaf * which would contains this node is retrieved. If it contains * vertices, the nearest one is returned (vertices in other leaves may * of course be nearer). Otherwise the nearest vertex from sibling * children is returned. The returned vertex is a good starting point * for {@link #getNearestVertex}. * * @param uv coordinates. * @return a near vertex. */ public final T getNearVertex(Metric metric, T uv) { if (root.nItems == 0) return null; Cell current = root; Cell last = null; int s = gridSize; int [] ijk = new int[dimension]; double2int(uv, ijk); int searchedCells = 0; if (logger.isLoggable(Level.FINE)) logger.fine("Near point: "+coordinatesToString(uv)); while (null != current && current.nItems < 0) { last = current; s >>= 1; assert s > 0; searchedCells++; current = (Cell) current.subCell[indexSubCell(ijk, s)]; } if (null == current) return getNearVertexInSubCells(last, metric, uv, searchedCells); T vQ = current.getVertex(0); T ret = vQ; double retdist = metric.distance2(uv, vQ); for (int i = 1; i < current.nItems; i++) { vQ = current.getVertex(i); double d = metric.distance2(uv, vQ); if (d < retdist) { retdist = d; ret = vQ; } } if (logger.isLoggable(Level.FINE)) logger.fine(" search in "+searchedCells+"/"+nCells+" cells"); return ret; } private T getNearVertexInSubCells(Cell current, Metric metric, T uv, int searchedCells) { T ret = null; int [] ijk = new int[dimension]; double dist = -1.0; double2int(uv, ijk); int l = 0; int [] posStack = new int[MAXLEVEL]; posStack[l] = 0; Object [] cellStack = new Object[MAXLEVEL]; cellStack[l] = current; while (true) { searchedCells++; Cell s = (Cell) cellStack[l]; if (s.nItems < 0) { l++; assert l <= MAXLEVEL; for (int i = 0; i < 8; i++) { if (null != s.subCell[i]) { cellStack[l] = s.subCell[i]; posStack[l] = i; break; } } } else { for (int i = 0; i < s.nItems; i++) { T vQ = s.getVertex(i); double d = metric.distance2(uv, vQ); if (d < dist || dist < 0.0) { dist = d; ret = vQ; } } if (null != ret) { if (logger.isLoggable(Level.FINE)) logger.fine(" search in "+searchedCells+"/"+nCells+" cells"); return ret; } // Search in siblings while (l > 0) { posStack[l]++; if (posStack[l] == 8) l--; else if (null != ((Cell) cellStack[l-1]).subCell[posStack[l]]) break; } if (l == 0) break; cellStack[l] = ((Cell) cellStack[l-1]).subCell[posStack[l]]; } } throw new RuntimeException("Near vertex not found"); } private final class GetNearestVertexProcedure implements KdTreeProcedure { private final int [] ijk = new int[dimension]; private final T fromPosition; private final Metric metric; private T nearestVertex; private final double [] i2d = new double[dimension]; private final int [] idist = new int[dimension]; private double dist; private int searchedCells; private GetNearestVertexProcedure(Metric m, T from, T v) { double2int(from, ijk); nearestVertex = v; fromPosition = from; metric = m; dist = metric.distance2(fromPosition, nearestVertex); double [] r = metric.getUnitBallBBox(); for (int k = 0; k < dimension; k++) { i2d[k] = 1.005 * x0[dimension] * r[k]; idist[k] = (int) (Math.sqrt(dist) * i2d[k]); if (idist[k] > Integer.MAX_VALUE/2) idist[k] = Integer.MAX_VALUE/2; } } public int action(Object o, int s, final int [] i0) { for (int k = 0; k < dimension; k++) if ((ijk[k] < i0[k] - idist[k]) || (ijk[k] > i0[k] + s + idist[k])) return KdTreeProcedure.SKIPCHILD; Cell self = (Cell) o; searchedCells++; if (self.nItems > 0) { boolean updated = false; for (int i = 0; i < self.nItems; i++) { T vtest = self.getVertex(i); double retdist = metric.distance2(fromPosition, vtest); if (retdist < dist) { dist = retdist; nearestVertex = vtest; updated = true; } } if (updated) { for (int k = 0; k < dimension; k++) { idist[k] = (int) (Math.sqrt(dist) * i2d[k]); if (idist[k] > Integer.MAX_VALUE/2) idist[k] = Integer.MAX_VALUE/2; } } } return KdTreeProcedure.OK; } } /** * Return the nearest vertex stored in this <code>KdTree</code>. * * @param uv coordinates. * @return the nearest vertex. */ public final T getNearestVertex(Metric metric, T uv) { if (root.nItems == 0) return null; T near = getNearVertex(metric, uv); if (logger.isLoggable(Level.FINE)) logger.fine("Nearest point of "+coordinatesToString(uv)); GetNearestVertexProcedure gproc = new GetNearestVertexProcedure(metric, uv, near); walk(gproc); T ret = gproc.nearestVertex; if (logger.isLoggable(Level.FINE)) { logger.fine(" search in "+gproc.searchedCells+"/"+nCells+" cells"); logger.fine(" result: "+ret); } return ret; } private final class GetNearestVertexDebugProcedure implements KdTreeProcedure { private final int [] ij = new int[dimension]; private double dist; private final T fromPosition; private T nearestVertex; private final Metric metric; private int searchedCells; private GetNearestVertexDebugProcedure(Metric m, T from, T v) { double2int(from, ij); nearestVertex = v; fromPosition = from; metric = m; dist = metric.distance2(fromPosition, v); } public int action(Object o, int s, final int [] i0) { Cell self = (Cell) o; searchedCells++; if (self.nItems > 0) { for (int i = 0; i < self.nItems; i++) { T vtest = self.getVertex(i); double retdist = metric.distance2(fromPosition, vtest); if (retdist < dist) { dist = retdist; nearestVertex = vtest; } } } return KdTreeProcedure.OK; } } /** * Slow implementation of {@link #getNearestVertex}. * This method should be called only for debugging purpose. * * @param uv coordinates. * @return the nearest vertex. */ public final T getNearestVertexDebug(Metric metric, T uv) { if (root.nItems == 0) return null; T ret = getNearVertex(metric, uv); assert ret != null; if (logger.isLoggable(Level.FINE)) logger.fine("(debug) Nearest point of "+coordinatesToString(uv)); GetNearestVertexDebugProcedure gproc = new GetNearestVertexDebugProcedure(metric, uv, ret); walk(gproc); ret = gproc.nearestVertex; if (logger.isLoggable(Level.FINE)) { logger.fine(" search in "+gproc.searchedCells+"/"+nCells+" cells"); logger.fine(" result: "+ret); } return ret; } private static final class GetMinSizeProcedure implements KdTreeProcedure { private int searchedCells; private int minSize = gridSize; public GetMinSizeProcedure() { } public int action(Object o, int s, final int [] i0) { searchedCells++; if (s < minSize) minSize = s; return KdTreeProcedure.OK; } } private int getMinSize() { GetMinSizeProcedure gproc = new GetMinSizeProcedure(); walk(gproc); int ret = gproc.minSize; if (logger.isLoggable(Level.FINE)) { logger.fine(" search in "+gproc.searchedCells+"/"+nCells+" cells"); logger.fine(" result: "+ret); } return ret; } public final int getMaxLevel() { int size = getMinSize(); int ret = 1; while (size < gridSize) { size <<= 1; ret++; } return ret; } // Methods only available in 2D public interface K2DInterface<T> { public long onLeft(T p0, T p1, T p2); public long distance2(T p0, T p1); public long distance2cached(T p1); } private final class K2DInvalid<T> implements K2DInterface<T> { public long onLeft(T p0, T p1, T p2) { throw new RuntimeException("Method onLeft only available in 2D"); } public long distance2(T p0, T p1) { throw new RuntimeException("Method distance2 only available in 2D"); } public long distance2cached(T p1) { throw new RuntimeException("Method distance2cached only available in 2D"); } } private final class K2D implements K2DInterface<T> { // Temporary arrays private final int[] i0 = new int[2]; private final int[] i1 = new int[2]; private final int[] i2 = new int[2]; public final long onLeft(T p0, T p1, T p2) { double2int(p0, i0); double2int(p1, i1); double2int(p2, i2); long x01 = i1[0] - i0[0]; long y01 = i1[1] - i0[1]; long x02 = i2[0] - i0[0]; long y02 = i2[1] - i0[1]; return x01 * y02 - x02 * y01; } public final long distance2(T p0, T p1) { double2int(p0, i0); double2int(p1, i1); long dx = i1[0] - i0[0]; long dy = i1[1] - i0[1]; return dx * dx + dy * dy; } public final long distance2cached(T p1) { double2int(p1, i1); long dx = i1[0] - i0[0]; long dy = i1[1] - i0[1]; return dx * dx + dy * dy; } } }