// ============================================================================
// File: ReversePathIterator.java
//
// Project: General utilities.
//
// Purpose: Is missing in java.awt.geom.
//
// Author: Rammi
//
// Copyright Notice: (c) 2005 Rammi (rammi@caff.de)
// This code is in the public domain.
// This usage of this code is allowed without any restrictions.
// No guarantees are given whatsoever.
//
// Latest change: $Date: 2005/02/08 11:59:19 $
//
// History: $Log: ReversePathIterator.java,v $
// History: Revision 1.3 2005/02/08 11:59:19 rammi
// History: Transferred into the public domain.
// History:
// History: Revision 1.2 2005/02/07 18:58:45 rammi
// History: Added optimizations
// History:
// History: Revision 1.1 2005/01/25 14:17:20 rammi
// History: First version
// History:
//=============================================================================
package de.caff.util;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.IllegalPathStateException;
/**
* A path iterator which iterates over a path in the reverse direction.
* This is missing in the java.awt.geom package, although it's quite simple to implement.
* After initialization the original PathIterator is not used any longer.
*
* There are several static convenience methods to create a reverse path iterator from
* a shape directly:
*
* - {@link #getReversePathIterator(java.awt.Shape)}
* for reversing the standard path iterator
* - {@link #getReversePathIterator(java.awt.Shape, double)}
* for reversing a flattened path iterator
* - {@link #getReversePathIterator(java.awt.Shape, java.awt.geom.AffineTransform)}
* for reversing a transformed path iterator
* - {@link #getReversePathIterator(java.awt.Shape, java.awt.geom.AffineTransform, double)}
* for reversing a transformed flattened path iterator
* - {@link #getReversePathIterator(java.awt.Shape, int)}
* for reversing the standard path iterator while explicitly defining a winding rule
* - {@link #getReversePathIterator(java.awt.Shape, double, int)}
* for reversing a flattened path iterator while explicitly defining a winding rule
* - {@link #getReversePathIterator(java.awt.Shape, java.awt.geom.AffineTransform, int)}
* for reversing a transformed path iterator while explicitly defining a winding rule
* - {@link #getReversePathIterator(java.awt.Shape, java.awt.geom.AffineTransform, double, int)}
* for reversing a transformed flattened path iterator while explicitly defining a winding rule
*
*
* @author Rammi
* @version $Revision: 1.3 $
*/
public class ReversePathIterator
implements PathIterator
{
/** The winding rule. */
private final int windingRule;
/** The reversed coordinates */
private final double[] coordinates;
/** The reversed segment types. */
private final int[] segmentTypes;
/** The index into the coordinates during iteration. */
private int coordIndex = 0;
/** The index into the segment types during iteration. */
private int segmentIndex = 0;
/**
* Get a reverse path iterator for a shape, keeping the shape's winding rule.
* @param shape shape for which a reverse path iterator is needed
* @return reverse path iterator
*/
public static PathIterator getReversePathIterator(Shape shape)
{
return new ReversePathIterator(shape.getPathIterator(null));
}
/**
* Get a reverse flattened path iterator for a shape, keeping the shape's winding rule.
* @param shape shape for which a reverse flattened path iterator is needed
* @param flatness flatness epsilon
* @return reverse flattened path iterator
*/
public static PathIterator getReversePathIterator(Shape shape, double flatness)
{
return new ReversePathIterator(shape.getPathIterator(null, flatness));
}
/**
* Get a reverse transformed path iterator for a shape, keeping the shape's winding rule.
* @param shape shape for which a reverse transformed path iterator is needed
* @return reverse transformed path iterator
*/
public static PathIterator getReversePathIterator(Shape shape, AffineTransform at)
{
return new ReversePathIterator(shape.getPathIterator(at));
}
/**
* Get a reverse transformed flattened path iterator for a shape, keeping the shape's winding rule.
* @param shape shape for which a reverse transformed flattened path iterator is needed
* @param flatness flatness epsilon
* @return reverse transformed flattened path iterator
*/
public static PathIterator getReversePathIterator(Shape shape, AffineTransform at, double flatness)
{
return new ReversePathIterator(shape.getPathIterator(at, flatness));
}
/**
* Get a reverse path iterator for a shape.
* @param shape shape for which a reverse path iterator is needed
* @param windingRule winding rule of newly created iterator
* @return reverse path iterator
*/
public static PathIterator getReversePathIterator(Shape shape, int windingRule)
{
return new ReversePathIterator(shape.getPathIterator(null), windingRule);
}
/**
* Get a reverse flattened path iterator for a shape.
* @param shape shape for which a reverse flattened path iterator is needed
* @param flatness flatness epsilon
* @param windingRule winding rule of newly created iterator
* @return reverse flattened path iterator
*/
public static PathIterator getReversePathIterator(Shape shape, double flatness, int windingRule)
{
return new ReversePathIterator(shape.getPathIterator(null, flatness), windingRule);
}
/**
* Get a reverse transformed path iterator for a shape.
* @param shape shape for which a reverse transformed path iterator is needed
* @param windingRule winding rule of newly created iterator
* @return reverse transformed path iterator
*/
public static PathIterator getReversePathIterator(Shape shape, AffineTransform at, int windingRule)
{
return new ReversePathIterator(shape.getPathIterator(at), windingRule);
}
/**
* Get a reverse transformed flattened path iterator for a shape.
* @param shape shape for which a reverse transformed flattened path iterator is needed
* @param flatness flatness epsilon
* @param windingRule winding rule of newly created iterator
* @return reverse transformed flattened path iterator
*/
public static PathIterator getReversePathIterator(Shape shape, AffineTransform at, double flatness, int windingRule)
{
return new ReversePathIterator(shape.getPathIterator(at, flatness), windingRule);
}
/**
* Create an inverted path iterator from a standard one, keeping the winding rule.
* @param original original iterator
*/
public ReversePathIterator(PathIterator original)
{
this(original, original.getWindingRule());
}
/**
* Create an inverted path iterator from a standard one.
* @param original original iterator
* @param windingRule winding rule of newly created iterator
*/
public ReversePathIterator(PathIterator original, int windingRule)
{
this.windingRule = windingRule;
double[] coords = new double[16];
int coordPos = 0;
int[] segTypes = new int[8];
int segPos = 0;
boolean first = true;
double[] temp = new double[6];
while (!original.isDone()) {
if (segPos == segTypes.length) {
// resize
int[] dummy = new int[2*segPos];
System.arraycopy(segTypes, 0, dummy, 0, segPos);
segTypes = dummy;
}
final int segType = segTypes[segPos++] = original.currentSegment(temp);
if (first) {
if (segType != SEG_MOVETO) {
throw new IllegalPathStateException("missing initial moveto in path definition");
}
first = false;
}
final int copy = coordinatesForSegmentType(segType);
if (copy > 0) {
if (coordPos + copy > coords.length) {
// resize
double[] dummy = new double[coords.length*2];
System.arraycopy(coords, 0, dummy, 0, coords.length);
coords = dummy;
}
for (int c = 0; c < copy; ++c) {
coords[coordPos++] = temp[c];
}
}
original.next();
}
// === reverse everything ===
// --- reverse coordinates ---
coordinates = new double[coordPos];
for (int p = coordPos/2-1; p >= 0; --p) {
coordinates[2*p ] = coords[coordPos-2*p-2];
coordinates[2*p+1] = coords[coordPos-2*p-1];
}
// --- reverse segment types ---
segmentTypes = new int[segPos];
if (segPos > 0) {
boolean pendingClose = false;
int sr = 0;
segmentTypes[sr++] = SEG_MOVETO;
for (int s = segPos-1; s > 0; --s) {
switch (segTypes[s]) {
case SEG_MOVETO:
if (pendingClose) {
pendingClose = false;
segmentTypes[sr++] = SEG_CLOSE;
}
segmentTypes[sr++] = SEG_MOVETO;
break;
case SEG_CLOSE:
pendingClose = true;
break;
default:
segmentTypes[sr++] = segTypes[s];
break;
}
}
if (pendingClose) {
segmentTypes[sr] = SEG_CLOSE;
}
}
}
/**
* Returns the winding rule for determining the interior of the
* path.
*
* @return the winding rule.
* @see #WIND_EVEN_ODD
* @see #WIND_NON_ZERO
*/
public int getWindingRule()
{
return windingRule;
}
/**
* Get the number of coordinates needed for a segment type.
* @param segtype segment type
* @return coordinates needed
*/
private static int coordinatesForSegmentType(int segtype)
{
switch (segtype) {
case SEG_MOVETO:
case SEG_LINETO:
return 2;
case SEG_QUADTO:
return 4;
case SEG_CUBICTO:
return 6;
}
return 0;
}
/**
* Moves the iterator to the next segment of the path forwards
* along the primary direction of traversal as long as there are
* more points in that direction.
*/
public void next()
{
coordIndex += coordinatesForSegmentType(segmentTypes[segmentIndex++]);
}
/**
* Tests if the iteration is complete.
*
* @return true
if all the segments have
* been read; false
otherwise.
*/
public boolean isDone()
{
return segmentIndex >= segmentTypes.length;
}
/**
* Returns the coordinates and type of the current path segment in
* the iteration.
* The return value is the path-segment type:
* SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE.
* A double array of length 6 must be passed in and can be used to
* store the coordinates of the point(s).
* Each point is stored as a pair of double x,y coordinates.
* SEG_MOVETO and SEG_LINETO types returns one point,
* SEG_QUADTO returns two points,
* SEG_CUBICTO returns 3 points
* and SEG_CLOSE does not return any points.
*
* @param coords an array that holds the data returned from
* this method
* @return the path-segment type of the current path segment.
* @see #SEG_MOVETO
* @see #SEG_LINETO
* @see #SEG_QUADTO
* @see #SEG_CUBICTO
* @see #SEG_CLOSE
*/
public int currentSegment(double[] coords)
{
final int segmentType = segmentTypes[segmentIndex];
final int copy = coordinatesForSegmentType(segmentType);
if (copy > 0) {
System.arraycopy(coordinates, coordIndex, coords, 0, copy);
}
return segmentType;
}
/**
* Returns the coordinates and type of the current path segment in
* the iteration.
* The return value is the path-segment type:
* SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE.
* A float array of length 6 must be passed in and can be used to
* store the coordinates of the point(s).
* Each point is stored as a pair of float x,y coordinates.
* SEG_MOVETO and SEG_LINETO types returns one point,
* SEG_QUADTO returns two points,
* SEG_CUBICTO returns 3 points
* and SEG_CLOSE does not return any points.
*
* @param coords an array that holds the data returned from
* this method
* @return the path-segment type of the current path segment.
* @see #SEG_MOVETO
* @see #SEG_LINETO
* @see #SEG_QUADTO
* @see #SEG_CUBICTO
* @see #SEG_CLOSE
*/
public int currentSegment(float[] coords)
{
final int segmentType = segmentTypes[segmentIndex];
final int copy = coordinatesForSegmentType(segmentType);
if (copy > 0) {
for (int c = 0; c < copy; ++c) {
coords[c] = (float)coordinates[coordIndex+c];
}
}
return segmentType;
}
}