/*
* @(#)BezierPathIterator.java
*
* Copyright (c) 1996-2010 The authors and contributors of JHotDraw.
* You may not use, copy or modify this file, except in compliance with the
* accompanying license terms.
*/
package org.jhotdraw.geom;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.awt.geom.*;
/**
* This class represents the iterator for a BezierPath.
* It can be used to retrieve all of the elements in a BezierPath.
* The {@link BezierPath#getPathIterator}
* method is used to create a
* BezierPathIterator for a particular BezierPath.
* The iterator can be used to iterator the path only once.
* Subsequent iterations require a new iterator.
*
* @author Werner Randelshofer
* @version $Id$
*/
public class BezierPathIterator implements PathIterator {
/**
* Index of the next node.
*/
private int index = 0;
/**
* The bezier path.
*/
private BezierPath path;
/**
* The transformation.
*/
@Nullable private AffineTransform affine;
/** ?? */
private static final int curvesize[] = {2, 2, 4, 6, 0};
/**
* Constructs an iterator given a BezierPath.
* @see BezierPath#getPathIterator
*/
public BezierPathIterator(BezierPath path) {
this(path, null);
}
/**
* Constructs an iterator given a BezierPath and an optional
* AffineTransform.
* @see BezierPath#getPathIterator
*/
public BezierPathIterator(BezierPath path, @Nullable AffineTransform at) {
this.path = path;
this.affine = at;
}
/**
* Return the winding rule for determining the interior of the
* path.
* @see PathIterator#WIND_EVEN_ODD
* @see PathIterator#WIND_NON_ZERO
*/
@Override
public int getWindingRule() {
return path.getWindingRule();
}
/**
* Tests if there are more points to read.
* @return true if there are more points to read
*/
@Override
public boolean isDone() {
return (index >= path.size() + (path.isClosed() ? 2 : 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.
*/
@Override
public void next() {
if (! isDone()) {
index++;
}
}
/**
* 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 may 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 will return one point,
* SEG_QUADTO will return two points,
* SEG_CUBICTO will return 3 points
* and SEG_CLOSE will not return any points.
* @see PathIterator#SEG_MOVETO
* @see PathIterator#SEG_LINETO
* @see PathIterator#SEG_QUADTO
* @see PathIterator#SEG_CUBICTO
* @see PathIterator#SEG_CLOSE
*/
@Override
public int currentSegment(float[] coords) {
int numCoords = 0;
int type = 0;
if (index == path.size()) {
// We only get here for closed paths
if (path.size() > 1) {
BezierPath.Node previous = path.get(path.size() - 1);
BezierPath.Node current = path.get(0);
if ((previous.mask & BezierPath.C2_MASK) == 0) {
if ((current.mask & BezierPath.C1_MASK) == 0) {
numCoords = 1;
type = SEG_LINETO;
coords[0] = (float) current.x[0];
coords[1] = (float) current.y[0];
} else {
numCoords = 2;
type = SEG_QUADTO;
coords[0] = (float) current.x[1];
coords[1] = (float) current.y[1];
coords[2] = (float) current.x[0];
coords[3] = (float) current.y[0];
}
} else {
if ((current.mask & BezierPath.C1_MASK) == 0) {
numCoords = 2;
type = SEG_QUADTO;
coords[0] = (float) previous.x[2];
coords[1] = (float) previous.y[2];
coords[2] = (float) current.x[0];
coords[3] = (float) current.y[0];
} else {
numCoords = 3;
type = SEG_CUBICTO;
coords[0] = (float) previous.x[2];
coords[1] = (float) previous.y[2];
coords[2] = (float) current.x[1];
coords[3] = (float) current.y[1];
coords[4] = (float) current.x[0];
coords[5] = (float) current.y[0];
}
}
}
} else if (index > path.size()) {
// We only get here for closed paths
return SEG_CLOSE;
} else if (index == 0) {
BezierPath.Node current = path.get(index);
coords[0] = (float) current.x[0];
coords[1] = (float) current.y[0];
numCoords = 1;
type = SEG_MOVETO;
} else if (index < path.size()) {
BezierPath.Node current = path.get(index);
BezierPath.Node previous = path.get(index - 1);
if ((previous.mask & BezierPath.C2_MASK) == 0) {
if ((current.mask & BezierPath.C1_MASK) == 0) {
numCoords = 1;
type = SEG_LINETO;
coords[0] = (float) current.x[0];
coords[1] = (float) current.y[0];
} else {
numCoords = 2;
type = SEG_QUADTO;
coords[0] = (float) current.x[1];
coords[1] = (float) current.y[1];
coords[2] = (float) current.x[0];
coords[3] = (float) current.y[0];
}
} else {
if ((current.mask & BezierPath.C1_MASK) == 0) {
numCoords = 2;
type = SEG_QUADTO;
coords[0] = (float) previous.x[2];
coords[1] = (float) previous.y[2];
coords[2] = (float) current.x[0];
coords[3] = (float) current.y[0];
} else {
numCoords = 3;
type = SEG_CUBICTO;
coords[0] = (float) previous.x[2];
coords[1] = (float) previous.y[2];
coords[2] = (float) current.x[1];
coords[3] = (float) current.y[1];
coords[4] = (float) current.x[0];
coords[5] = (float) current.y[0];
}
}
}
if (affine != null) {
affine.transform(coords, 0, coords, 0, numCoords);
}
return type;
}
/**
* 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 may 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 will return one point,
* SEG_QUADTO will return two points,
* SEG_CUBICTO will return 3 points
* and SEG_CLOSE will not return any points.
* @see PathIterator#SEG_MOVETO
* @see PathIterator#SEG_LINETO
* @see PathIterator#SEG_QUADTO
* @see PathIterator#SEG_CUBICTO
* @see PathIterator#SEG_CLOSE
*/
@Override
public int currentSegment(double[] coords) {
int numCoords = 0;
int type = 0;
if (index == path.size()) {
// We only get here for closed paths
if (path.size() > 1) {
BezierPath.Node previous = path.get(path.size() - 1);
BezierPath.Node current = path.get(0);
if ((previous.mask & BezierPath.C2_MASK) == 0) {
if ((current.mask & BezierPath.C1_MASK) == 0) {
numCoords = 1;
type = SEG_LINETO;
coords[0] = current.x[0];
coords[1] = current.y[0];
} else {
numCoords = 2;
type = SEG_QUADTO;
coords[0] = current.x[1];
coords[1] = current.y[1];
coords[2] = current.x[0];
coords[3] = current.y[0];
}
} else {
if ((current.mask & BezierPath.C1_MASK) == 0) {
numCoords = 2;
type = SEG_QUADTO;
coords[0] = previous.x[2];
coords[1] = previous.y[2];
coords[2] = current.x[0];
coords[3] = current.y[0];
} else {
numCoords = 3;
type = SEG_CUBICTO;
coords[0] = previous.x[2];
coords[1] = previous.y[2];
coords[2] = current.x[1];
coords[3] = current.y[1];
coords[4] = current.x[0];
coords[5] = current.y[0];
}
}
}
} else if (index > path.size()) {
// We only get here for closed paths
return SEG_CLOSE;
} else if (index == 0) {
BezierPath.Node current = path.get(index);
coords[0] = current.x[0];
coords[1] = current.y[0];
numCoords = 1;
type = SEG_MOVETO;
} else if (index < path.size()) {
BezierPath.Node current = path.get(index);
BezierPath.Node previous = path.get(index - 1);
if ((previous.mask & BezierPath.C2_MASK) == 0) {
if ((current.mask & BezierPath.C1_MASK) == 0) {
numCoords = 1;
type = SEG_LINETO;
coords[0] = current.x[0];
coords[1] = current.y[0];
} else {
numCoords = 2;
type = SEG_QUADTO;
coords[0] = current.x[1];
coords[1] = current.y[1];
coords[2] = current.x[0];
coords[3] = current.y[0];
}
} else {
if ((current.mask & BezierPath.C1_MASK) == 0) {
numCoords = 2;
type = SEG_QUADTO;
coords[0] = previous.x[2];
coords[1] = previous.y[2];
coords[2] = current.x[0];
coords[3] = current.y[0];
} else {
numCoords = 3;
type = SEG_CUBICTO;
coords[0] = previous.x[2];
coords[1] = previous.y[2];
coords[2] = current.x[1];
coords[3] = current.y[1];
coords[4] = current.x[0];
coords[5] = current.y[0];
}
}
}
if (affine != null) {
affine.transform(coords, 0, coords, 0, numCoords);
} else {
System.arraycopy(coords, 0, coords, 0, numCoords);
}
return type;
}
}