blob: ca5c7c2f9bf4883d36db49307b966c0127b5fb06 [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 withing the flattening factor of the curve or until the buffer
* limit is reached.
*/
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 tamporary 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 withing 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 serries.
* 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;
}
}