blob: 8208f39635f8c940d5826007bf7188826fe8003d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Denis M. Kishenko
* @version $Revision$
*/
package java.awt.geom;
import java.util.NoSuchElementException;
import org.apache.harmony.awt.internal.nls.Messages;
/**
* The Class FlatteningPathIterator takes a PathIterator for traversing a curved
* shape and flattens it by estimating the curve as a series of line segments.
* The flattening factor indicates how far the estimating line segments are
* allowed to be from the actual curve: the FlatteningPathIterator will keep
* dividing each curved segment into smaller and smaller flat segments until
* either the segments are within the flattening factor of the curve or until
* the buffer limit is reached.
*
* @since Android 1.0
*/
public class FlatteningPathIterator implements PathIterator {
/**
* The default points buffer size.
*/
private static final int BUFFER_SIZE = 16;
/**
* The default curve subdivision limit.
*/
private static final int BUFFER_LIMIT = 16;
/**
* The points buffer capacity.
*/
private static final int BUFFER_CAPACITY = 16;
/**
* The type of current segment to be flat.
*/
int bufType;
/**
* The curve subdivision limit.
*/
int bufLimit;
/**
* The current points buffer size.
*/
int bufSize;
/**
* The inner cursor position in points buffer.
*/
int bufIndex;
/**
* The current subdivision count.
*/
int bufSubdiv;
/**
* The points buffer.
*/
double buf[];
/**
* The indicator of empty points buffer.
*/
boolean bufEmpty = true;
/**
* The source PathIterator.
*/
PathIterator p;
/**
* The flatness of new path.
*/
double flatness;
/**
* The square of flatness.
*/
double flatness2;
/**
* The x coordinate of previous path segment.
*/
double px;
/**
* The y coordinate of previous path segment.
*/
double py;
/**
* The temporary buffer for getting points from PathIterator.
*/
double coords[] = new double[6];
/**
* Instantiates a new flattening path iterator given the path iterator for a
* (possibly) curved path and a flattening factor which indicates how close
* together the points on the curve should be chosen. The buffer limit
* defaults to 16 which means that each curve will be divided into no more
* than 16 segments regardless of the flattening factor.
*
* @param path
* the path iterator of the original curve.
* @param flatness
* the flattening factor that indicates how far the flat path is
* allowed to be from the actual curve in order to decide when to
* stop dividing the path into smaller and smaller segments.
* @throws IllegalArgumentException
* if the flatness is less than zero.
* @throws NullPointerException
* if the path is null.
*/
public FlatteningPathIterator(PathIterator path, double flatness) {
this(path, flatness, BUFFER_LIMIT);
}
/**
* Instantiates a new flattening path iterator given the path iterator for a
* (possibly) curved path and a flattening factor and a buffer limit. The
* FlatteningPathIterator will keep dividing each curved segment into
* smaller and smaller flat segments until either the segments are within
* the flattening factor of the curve or until the buffer limit is reached.
*
* @param path
* the path iterator of the original curve.
* @param flatness
* the flattening factor that indicates how far the flat path is
* allowed to be from the actual curve in order to decide when to
* stop dividing the path into smaller and smaller segments.
* @param limit
* the maximum number of flat segments to divide each curve into.
* @throws IllegalArgumentException
* if the flatness or limit is less than zero.
* @throws NullPointerException
* if the path is null.
*/
public FlatteningPathIterator(PathIterator path, double flatness, int limit) {
if (flatness < 0.0) {
// awt.206=Flatness is less then zero
throw new IllegalArgumentException(Messages.getString("awt.206")); //$NON-NLS-1$
}
if (limit < 0) {
// awt.207=Limit is less then zero
throw new IllegalArgumentException(Messages.getString("awt.207")); //$NON-NLS-1$
}
if (path == null) {
// awt.208=Path is null
throw new NullPointerException(Messages.getString("awt.208")); //$NON-NLS-1$
}
this.p = path;
this.flatness = flatness;
this.flatness2 = flatness * flatness;
this.bufLimit = limit;
this.bufSize = Math.min(bufLimit, BUFFER_SIZE);
this.buf = new double[bufSize];
this.bufIndex = bufSize;
}
/**
* Gets the flattening factor.
*
* @return the flattening factor.
*/
public double getFlatness() {
return flatness;
}
/**
* Gets the maximum number of subdivisions per curved segment.
*
* @return the maximum number of subdivisions per curved segment.
*/
public int getRecursionLimit() {
return bufLimit;
}
public int getWindingRule() {
return p.getWindingRule();
}
public boolean isDone() {
return bufEmpty && p.isDone();
}
/**
* Calculates flat path points for current segment of the source shape. Line
* segment is flat by itself. Flatness of quad and cubic curves evaluated by
* getFlatnessSq() method. Curves subdivided until current flatness is
* bigger than user defined and subdivision limit isn't exhausted. Single
* source segment translated to series of buffer points. The less flatness
* the bigger series. Every currentSegment() call extract one point from the
* buffer. When series completed evaluate() takes next source shape segment.
*/
void evaluate() {
if (bufEmpty) {
bufType = p.currentSegment(coords);
}
switch (bufType) {
case SEG_MOVETO:
case SEG_LINETO:
px = coords[0];
py = coords[1];
break;
case SEG_QUADTO:
if (bufEmpty) {
bufIndex -= 6;
buf[bufIndex + 0] = px;
buf[bufIndex + 1] = py;
System.arraycopy(coords, 0, buf, bufIndex + 2, 4);
bufSubdiv = 0;
}
while (bufSubdiv < bufLimit) {
if (QuadCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) {
break;
}
// Realloc buffer
if (bufIndex <= 4) {
double tmp[] = new double[bufSize + BUFFER_CAPACITY];
System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize
- bufIndex);
buf = tmp;
bufSize += BUFFER_CAPACITY;
bufIndex += BUFFER_CAPACITY;
}
QuadCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 4, buf, bufIndex);
bufIndex -= 4;
bufSubdiv++;
}
bufIndex += 4;
px = buf[bufIndex];
py = buf[bufIndex + 1];
bufEmpty = (bufIndex == bufSize - 2);
if (bufEmpty) {
bufIndex = bufSize;
bufType = SEG_LINETO;
} else {
bufSubdiv--;
}
break;
case SEG_CUBICTO:
if (bufEmpty) {
bufIndex -= 8;
buf[bufIndex + 0] = px;
buf[bufIndex + 1] = py;
System.arraycopy(coords, 0, buf, bufIndex + 2, 6);
bufSubdiv = 0;
}
while (bufSubdiv < bufLimit) {
if (CubicCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) {
break;
}
// Realloc buffer
if (bufIndex <= 6) {
double tmp[] = new double[bufSize + BUFFER_CAPACITY];
System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize
- bufIndex);
buf = tmp;
bufSize += BUFFER_CAPACITY;
bufIndex += BUFFER_CAPACITY;
}
CubicCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 6, buf, bufIndex);
bufIndex -= 6;
bufSubdiv++;
}
bufIndex += 6;
px = buf[bufIndex];
py = buf[bufIndex + 1];
bufEmpty = (bufIndex == bufSize - 2);
if (bufEmpty) {
bufIndex = bufSize;
bufType = SEG_LINETO;
} else {
bufSubdiv--;
}
break;
}
}
public void next() {
if (bufEmpty) {
p.next();
}
}
public int currentSegment(float[] coords) {
if (isDone()) {
// awt.4B=Iterator out of bounds
throw new NoSuchElementException(Messages.getString("awt.4Bx")); //$NON-NLS-1$
}
evaluate();
int type = bufType;
if (type != SEG_CLOSE) {
coords[0] = (float)px;
coords[1] = (float)py;
if (type != SEG_MOVETO) {
type = SEG_LINETO;
}
}
return type;
}
public int currentSegment(double[] coords) {
if (isDone()) {
// awt.4B=Iterator out of bounds
throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
}
evaluate();
int type = bufType;
if (type != SEG_CLOSE) {
coords[0] = px;
coords[1] = py;
if (type != SEG_MOVETO) {
type = SEG_LINETO;
}
}
return type;
}
}