359 lines
11 KiB
Java
359 lines
11 KiB
Java
/*
|
|
* 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;
|
|
}
|
|
}
|