/* * 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; } }