// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.turnlanes.gui;
import java.awt.geom.PathIterator;
import java.util.Arrays;
/**
* Path iterator which reverses the path of a given iterator.
*
* <p>
* The given (unreversed) iterator must start with a {@code PathIterator.SEG_MOVETO} and must not
* contain any {@code PathIterator.SEG_CLOSE}. This class is intended for use with iterators
* returned by {@code Path.getIterator} which has exactly those properties.
* </p>
*
* @author Ben Schulz
*/
final class ReversePathIterator implements PathIterator {
private static final int[] COUNTS = {
2, // SEG_MOVETO = 0
2, // SEG_LINETO = 1
4, // SEG_QUADTO = 2
6, // SEG_CUBICTO = 3
0, // SEG_CLOSE = 4
};
public static ReversePathIterator reverse(PathIterator it) {
return new ReversePathIterator(it);
}
private static int[] reverseTypes(int[] types, int length) {
if (length > 0 && types[0] != SEG_MOVETO) {
// the last segment of the reversed path is not defined
throw new IllegalArgumentException("Can not reverse path without initial SEG_MOVETO.");
}
final int[] result = new int[length];
result[0] = SEG_MOVETO;
int lower = 1;
int upper = length - 1;
while (lower <= upper) {
result[lower] = types[upper];
result[upper] = types[lower];
++lower;
--upper;
}
return result;
}
private static double[] reverseCoords(double[] coords, int length) {
final double[] result = new double[length];
int lower = 0;
int upper = length - 2;
while (lower <= upper) {
result[lower] = coords[upper];
result[lower + 1] = coords[upper + 1];
result[upper] = coords[lower];
result[upper + 1] = coords[lower + 1];
lower += 2;
upper -= 2;
}
return result;
}
private final int winding;
private final int[] types;
private int typesIndex = 0;
private final double[] coords;
private int coordsIndex = 0;
private ReversePathIterator(PathIterator it) {
this.winding = it.getWindingRule();
double[] tmpCoords = new double[62];
int[] tmpTypes = new int[11];
int tmpCoordsI = 0;
int tmpTypesI = 0;
while (!it.isDone()) {
if (tmpTypesI >= tmpTypes.length) {
tmpTypes = Arrays.copyOf(tmpTypes, 2 * tmpTypes.length);
}
final double[] cs = new double[6];
final int t = it.currentSegment(cs);
tmpTypes[tmpTypesI++] = t;
final int count = COUNTS[t];
if (tmpCoordsI + count > tmpCoords.length) {
tmpCoords = Arrays.copyOf(tmpCoords, 2 * tmpCoords.length);
}
System.arraycopy(cs, 0, tmpCoords, tmpCoordsI, count);
tmpCoordsI += count;
it.next();
}
this.types = reverseTypes(tmpTypes, tmpTypesI);
this.coords = reverseCoords(tmpCoords, tmpCoordsI);
}
@Override
public int getWindingRule() {
return winding;
}
@Override
public boolean isDone() {
return typesIndex >= types.length;
}
@Override
public void next() {
coordsIndex += COUNTS[types[typesIndex]];
++typesIndex;
}
@Override
public int currentSegment(float[] coords) {
final double[] tmp = new double[6];
final int type = currentSegment(tmp);
coords[0] = (float) tmp[0];
coords[1] = (float) tmp[1];
coords[2] = (float) tmp[2];
coords[3] = (float) tmp[3];
coords[4] = (float) tmp[4];
coords[5] = (float) tmp[5];
return type;
}
@Override
public int currentSegment(double[] coords) {
final int type = types[typesIndex];
System.arraycopy(this.coords, coordsIndex, coords, 0, COUNTS[type]);
return type;
}
}