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