/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-2012, Geomatys
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotoolkit.path.iterator;
import java.util.Arrays;
import java.util.Iterator;
/**
* Iterator which follow multi-dimensional Hilbert curve.
*
* @author RĂ©mi Marechal (Geomatys).
* @author Martin Desruisseaux (Geomatys).
*/
public class HilbertIterator implements Iterator<int[]> {
private final HilbertIterator subOrder;
private final boolean[] sign;
private final int[]ordinates;
private final int[]coordinates;
private final int l;
private final int nbCells;
private final int hilbertOrder;
private final int dimension;
private final int modulo;
private int compteurCells = 0;
private int currentPos = 0;
private int compteurJoin = 0;
private int joinInf = 1;
private int ordinate;
private int currentSign;
private boolean signVal;
private boolean oneTime = true;
private boolean firstTime = true;
/**
* Create Iterator to build Hilbert curve.
*
* @param hilbertOrder Hilbert curve order.
* @param dimension : space dimension where curve is define.
*/
public HilbertIterator(int hilbertOrder, int dimension) {
this(hilbertOrder, dimension, true);
}
/**
* Create Iterator to build Hilbert curve.
*
* @param hilbertOrder Hilbert curve order.
* @param dimension : space dimension where curve is define.
* @param superParent : define which HilbertIterator is tallest.
*/
private HilbertIterator(int hilbertOrder, int dimension, boolean superParent) {
this.hilbertOrder = hilbertOrder;
this.dimension = dimension;
nbCells = 2 << (dimension*hilbertOrder - 1);
modulo = 2 << dimension-1;
l = modulo - 1;
ordinates = generateBasicOrd(dimension-1, new int[]{dimension-1,dimension-2,dimension-1});
if (superParent) {
sign = new boolean[dimension];
Arrays.fill(sign, true);
} else {
sign = null;
}
coordinates = new int[dimension];
subOrder = (hilbertOrder != 1) ? new HilbertIterator(hilbertOrder-1, dimension, false) : null;
}
/**
* Create appropriate first Hilbert curve path iteration.
*
* @param remainingDim space dimension.
* @param currentlyPath
* @return order 1 Hilbert curve path.
*/
private int[] generateBasicOrd(int remainingDim, int[] currentlyPath) {
if (remainingDim == 1) return currentlyPath;
final int length = currentlyPath.length;
final int size = 2*length+1;
final int[] path = new int[size];
System.arraycopy(currentlyPath.clone(), 0, path, 0, length);
path[length] = remainingDim-2;
System.arraycopy(currentlyPath.clone(), 0, path, length+1, length);
return generateBasicOrd(remainingDim-1, path);
}
/**
* Appropriate Hilbert ordinate operation.
*
* @param val current sub-order ordinate.
* @param operande current ordinate.
*/
private int oLogic(int val, final int operande) {
return Math.abs((val + operande) % dimension);
}
/**
* Return true if next Hilbert iteration is possible.
*
* @return true if next iterate exit else false.
*/
private boolean hasNextOperande() {
if (subOrder == null) return currentPos < l;
return (compteurJoin < l || subOrder.hasNextOperande());
}
/**
* Compute which ordinate is followed by Hilbert curve and its sens.
*/
private void nextOperande() {
if (hilbertOrder == 1) {
ordinate = ordinates[currentPos];
currentPos++;
if (sign != null) {
signVal = sign[ordinate];
sign[ordinate] = !sign[ordinate];
}
} else {
if (subOrder.hasNextOperande()) {
subOrder.nextOperande();
ordinate = oLogic(subOrder.ordinate, ordinates[currentPos]);
if (sign != null) {
sign[ordinate] = !sign[ordinate];
signVal = (joinInf % modulo == 0) ? sign[ordinate] : !sign[ordinate];
}
joinInf++;
} else {
ordinate = ordinates[compteurJoin];
compteurJoin++;
joinInf = 1;
if (currentPos > 0 && currentPos < l-1) {
oneTime = !oneTime;
if(oneTime) currentPos += (currentPos != l-2) ? 2 : 1;
} else {
currentPos++;
}
if (sign != null) {
sign[ordinate] = !sign[ordinate];
signVal = sign[ordinate];
}
if (compteurJoin < l+1) subOrder.rewind();
}
}
}
/**
* Return next Hilbert curve point coordinates.
*
* @return next Hilbert curve point coordinates.
*/
@Override
public int[] next() {
compteurCells++;
if (firstTime) {
firstTime = false;
return coordinates.clone();
}
nextOperande();
currentSign = (signVal) ? 1 : -1;
coordinates[ordinate] += currentSign;
return coordinates.clone();
}
/**
* Initialize HilbertIterator.
*/
public void rewind() {
currentPos = compteurJoin = 0;
oneTime = true;
joinInf = 1;
if (sign != null) Arrays.fill(sign, true);
if (subOrder != null) subOrder.rewind();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not used.");
}
/**
* Returns true if a next Hilbert curve point will be able to compute else false.
* In other words if {@link HilbertIterator#next() }.
*
* @return true if a next point will be able to compute else false.
*/
@Override
public boolean hasNext() {
return compteurCells < nbCells;
}
}