diff options
Diffstat (limited to 'awt/java/awt/geom/GeneralPath.java')
-rw-r--r-- | awt/java/awt/geom/GeneralPath.java | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/awt/java/awt/geom/GeneralPath.java b/awt/java/awt/geom/GeneralPath.java new file mode 100644 index 0000000..0669bc7 --- /dev/null +++ b/awt/java/awt/geom/GeneralPath.java @@ -0,0 +1,624 @@ +/* + * 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.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.gl.Crossing; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The class GeneralPath represents a shape whose outline is given by different + * types of curved and straight segments. + * + * @since Android 1.0 + */ +public final class GeneralPath implements Shape, Cloneable { + + /** + * The Constant WIND_EVEN_ODD see {@link PathIterator#WIND_EVEN_ODD}. + */ + public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; + + /** + * The Constant WIND_NON_ZERO see {@link PathIterator#WIND_NON_ZERO}. + */ + public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; + + /** + * The buffers size. + */ + private static final int BUFFER_SIZE = 10; + + /** + * The buffers capacity. + */ + private static final int BUFFER_CAPACITY = 10; + + /** + * The point's types buffer. + */ + byte[] types; + + /** + * The points buffer. + */ + float[] points; + + /** + * The point's type buffer size. + */ + int typeSize; + + /** + * The points buffer size. + */ + int pointSize; + + /** + * The path rule. + */ + int rule; + + /** + * The space amount in points buffer for different segmenet's types. + */ + static int pointShift[] = { + 2, // MOVETO + 2, // LINETO + 4, // QUADTO + 6, // CUBICTO + 0 + }; // CLOSE + + /* + * GeneralPath path iterator + */ + /** + * The Class Iterator is the subclass of Iterator for traversing the outline + * of a GeneralPath. + */ + class Iterator implements PathIterator { + + /** + * The current cursor position in types buffer. + */ + int typeIndex; + + /** + * The current cursor position in points buffer. + */ + int pointIndex; + + /** + * The source GeneralPath object. + */ + GeneralPath p; + + /** + * The path iterator transformation. + */ + AffineTransform t; + + /** + * Constructs a new GeneralPath.Iterator for given general path. + * + * @param path + * the source GeneralPath object. + */ + Iterator(GeneralPath path) { + this(path, null); + } + + /** + * Constructs a new GeneralPath.Iterator for given general path and + * transformation. + * + * @param path + * the source GeneralPath object. + * @param at + * the AffineTransform object to apply rectangle path. + */ + Iterator(GeneralPath path, AffineTransform at) { + this.p = path; + this.t = at; + } + + public int getWindingRule() { + return p.getWindingRule(); + } + + public boolean isDone() { + return typeIndex >= p.typeSize; + } + + public void next() { + typeIndex++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type = p.types[typeIndex]; + int count = GeneralPath.pointShift[type]; + for (int i = 0; i < count; i++) { + coords[i] = p.points[pointIndex + i]; + } + if (t != null) { + t.transform(coords, 0, coords, 0, count / 2); + } + pointIndex += count; + return type; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type = p.types[typeIndex]; + int count = GeneralPath.pointShift[type]; + System.arraycopy(p.points, pointIndex, coords, 0, count); + if (t != null) { + t.transform(coords, 0, coords, 0, count / 2); + } + pointIndex += count; + return type; + } + + } + + /** + * Instantiates a new general path with the winding rule set to + * {@link PathIterator#WIND_NON_ZERO} and the initial capacity (number of + * segments) set to the default value 10. + */ + public GeneralPath() { + this(WIND_NON_ZERO, BUFFER_SIZE); + } + + /** + * Instantiates a new general path with the given winding rule and the + * initial capacity (number of segments) set to the default value 10. + * + * @param rule + * the winding rule, either {@link PathIterator#WIND_EVEN_ODD} or + * {@link PathIterator#WIND_NON_ZERO}. + */ + public GeneralPath(int rule) { + this(rule, BUFFER_SIZE); + } + + /** + * Instantiates a new general path with the given winding rule and initial + * capacity (number of segments). + * + * @param rule + * the winding rule, either {@link PathIterator#WIND_EVEN_ODD} or + * {@link PathIterator#WIND_NON_ZERO}. + * @param initialCapacity + * the number of segments the path is set to hold. + */ + public GeneralPath(int rule, int initialCapacity) { + setWindingRule(rule); + types = new byte[initialCapacity]; + points = new float[initialCapacity * 2]; + } + + /** + * Creates a new GeneralPath from the outline of the given shape. + * + * @param shape + * the shape. + */ + public GeneralPath(Shape shape) { + this(WIND_NON_ZERO, BUFFER_SIZE); + PathIterator p = shape.getPathIterator(null); + setWindingRule(p.getWindingRule()); + append(p, false); + } + + /** + * Sets the winding rule, which determines how to decide whether a point + * that isn't on the path itself is inside or outside of the shape. + * + * @param rule + * the new winding rule. + * @throws IllegalArgumentException + * if the winding rule is neither + * {@link PathIterator#WIND_EVEN_ODD} nor + * {@link PathIterator#WIND_NON_ZERO}. + */ + public void setWindingRule(int rule) { + if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) { + // awt.209=Invalid winding rule value + throw new java.lang.IllegalArgumentException(Messages.getString("awt.209")); //$NON-NLS-1$ + } + this.rule = rule; + } + + /** + * Gets the winding rule. + * + * @return the winding rule, either {@link PathIterator#WIND_EVEN_ODD} or + * {@link PathIterator#WIND_NON_ZERO}. + */ + public int getWindingRule() { + return rule; + } + + /** + * Checks the point data buffer sizes to see whether pointCount additional + * point-data elements can fit. (Note that the number of point data elements + * to add is more than one per point -- it depends on the type of point + * being added.) Reallocates the buffers to enlarge the size if necessary. + * + * @param pointCount + * the number of point data elements to be added. + * @param checkMove + * whether to check for existing points. + * @throws IllegalPathStateException + * checkMove is true and the path is currently empty. + */ + void checkBuf(int pointCount, boolean checkMove) { + if (checkMove && typeSize == 0) { + // awt.20A=First segment should be SEG_MOVETO type + throw new IllegalPathStateException(Messages.getString("awt.20A")); //$NON-NLS-1$ + } + if (typeSize == types.length) { + byte tmp[] = new byte[typeSize + BUFFER_CAPACITY]; + System.arraycopy(types, 0, tmp, 0, typeSize); + types = tmp; + } + if (pointSize + pointCount > points.length) { + float tmp[] = new float[pointSize + Math.max(BUFFER_CAPACITY * 2, pointCount)]; + System.arraycopy(points, 0, tmp, 0, pointSize); + points = tmp; + } + } + + /** + * Appends a new point to the end of this general path, disconnected from + * the existing path. + * + * @param x + * the x coordinate of the next point to append. + * @param y + * the y coordinate of the next point to append. + */ + public void moveTo(float x, float y) { + if (typeSize > 0 && types[typeSize - 1] == PathIterator.SEG_MOVETO) { + points[pointSize - 2] = x; + points[pointSize - 1] = y; + } else { + checkBuf(2, false); + types[typeSize++] = PathIterator.SEG_MOVETO; + points[pointSize++] = x; + points[pointSize++] = y; + } + } + + /** + * Appends a new segment to the end of this general path by making a + * straight line segment from the current endpoint to the given new point. + * + * @param x + * the x coordinate of the next point to append. + * @param y + * the y coordinate of the next point to append. + */ + public void lineTo(float x, float y) { + checkBuf(2, true); + types[typeSize++] = PathIterator.SEG_LINETO; + points[pointSize++] = x; + points[pointSize++] = y; + } + + /** + * Appends a new segment to the end of this general path by making a + * quadratic curve from the current endpoint to the point (x2, y2) using the + * point (x1, y1) as the quadratic curve's control point. + * + * @param x1 + * the x coordinate of the quadratic curve's control point. + * @param y1 + * the y coordinate of the quadratic curve's control point. + * @param x2 + * the x coordinate of the quadratic curve's end point. + * @param y2 + * the y coordinate of the quadratic curve's end point. + */ + public void quadTo(float x1, float y1, float x2, float y2) { + checkBuf(4, true); + types[typeSize++] = PathIterator.SEG_QUADTO; + points[pointSize++] = x1; + points[pointSize++] = y1; + points[pointSize++] = x2; + points[pointSize++] = y2; + } + + /** + * Appends a new segment to the end of this general path by making a cubic + * curve from the current endpoint to the point (x3, y3) using (x1, y1) and + * (x2, y2) as control points. + * + * @see java.awt.geom.CubicCurve2D + * @param x1 + * the x coordinate of the new cubic segment's first control + * point. + * @param y1 + * the y coordinate of the new cubic segment's first control + * point. + * @param x2 + * the x coordinate of the new cubic segment's second control + * point. + * @param y2 + * the y coordinate of the new cubic segment's second control + * point. + * @param x3 + * the x coordinate of the new cubic segment's end point. + * @param y3 + * the y coordinate of the new cubic segment's end point. + */ + public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) { + checkBuf(6, true); + types[typeSize++] = PathIterator.SEG_CUBICTO; + points[pointSize++] = x1; + points[pointSize++] = y1; + points[pointSize++] = x2; + points[pointSize++] = y2; + points[pointSize++] = x3; + points[pointSize++] = y3; + } + + /** + * Appends the type information to declare that the current endpoint closes + * the curve. + */ + public void closePath() { + if (typeSize == 0 || types[typeSize - 1] != PathIterator.SEG_CLOSE) { + checkBuf(0, true); + types[typeSize++] = PathIterator.SEG_CLOSE; + } + } + + /** + * Appends the outline of the specified shape onto the end of this + * GeneralPath. + * + * @param shape + * the shape whose outline is to be appended. + * @param connect + * true to connect this path's current endpoint to the first + * point of the shape's outline or false to append the shape's + * outline without connecting it. + * @throws NullPointerException + * if the shape parameter is null. + */ + public void append(Shape shape, boolean connect) { + PathIterator p = shape.getPathIterator(null); + append(p, connect); + } + + /** + * Appends the path defined by the specified PathIterator onto the end of + * this GeneralPath. + * + * @param path + * the PathIterator that defines the new path to append. + * @param connect + * true to connect this path's current endpoint to the first + * point of the shape's outline or false to append the shape's + * outline without connecting it. + */ + public void append(PathIterator path, boolean connect) { + while (!path.isDone()) { + float coords[] = new float[6]; + switch (path.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + if (!connect || typeSize == 0) { + moveTo(coords[0], coords[1]); + break; + } + if (types[typeSize - 1] != PathIterator.SEG_CLOSE + && points[pointSize - 2] == coords[0] + && points[pointSize - 1] == coords[1]) { + break; + } + // NO BREAK; + case PathIterator.SEG_LINETO: + lineTo(coords[0], coords[1]); + break; + case PathIterator.SEG_QUADTO: + quadTo(coords[0], coords[1], coords[2], coords[3]); + break; + case PathIterator.SEG_CUBICTO: + curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); + break; + case PathIterator.SEG_CLOSE: + closePath(); + break; + } + path.next(); + connect = false; + } + } + + /** + * Gets the current end point of the path. + * + * @return the current end point of the path. + */ + public Point2D getCurrentPoint() { + if (typeSize == 0) { + return null; + } + int j = pointSize - 2; + if (types[typeSize - 1] == PathIterator.SEG_CLOSE) { + + for (int i = typeSize - 2; i > 0; i--) { + int type = types[i]; + if (type == PathIterator.SEG_MOVETO) { + break; + } + j -= pointShift[type]; + } + } + return new Point2D.Float(points[j], points[j + 1]); + } + + /** + * Resets the GeneralPath to being an empty path. The underlying point and + * segment data is not deleted but rather the end indices of the data arrays + * are set to zero. + */ + public void reset() { + typeSize = 0; + pointSize = 0; + } + + /** + * Transform all of the coordinates of this path according to the specified + * AffineTransform. + * + * @param t + * the AffineTransform. + */ + public void transform(AffineTransform t) { + t.transform(points, 0, points, 0, pointSize / 2); + } + + /** + * Creates a new GeneralPath whose data is given by this path's data + * transformed according to the specified AffineTransform. + * + * @param t + * the AffineTransform. + * @return the new GeneralPath whose data is given by this path's data + * transformed according to the specified AffineTransform. + */ + public Shape createTransformedShape(AffineTransform t) { + GeneralPath p = (GeneralPath)clone(); + if (t != null) { + p.transform(t); + } + return p; + } + + public Rectangle2D getBounds2D() { + float rx1, ry1, rx2, ry2; + if (pointSize == 0) { + rx1 = ry1 = rx2 = ry2 = 0.0f; + } else { + int i = pointSize - 1; + ry1 = ry2 = points[i--]; + rx1 = rx2 = points[i--]; + while (i > 0) { + float y = points[i--]; + float x = points[i--]; + if (x < rx1) { + rx1 = x; + } else if (x > rx2) { + rx2 = x; + } + if (y < ry1) { + ry1 = y; + } else if (y > ry2) { + ry2 = y; + } + } + } + return new Rectangle2D.Float(rx1, ry1, rx2 - rx1, ry2 - ry1); + } + + public Rectangle getBounds() { + return getBounds2D().getBounds(); + } + + /** + * Checks the cross count (number of times a ray from the point crosses the + * shape's boundary) to determine whether the number of crossings + * corresponds to a point inside the shape or not (according to the shape's + * path rule). + * + * @param cross + * the point's cross count. + * @return true if the point is inside the path, or false otherwise. + */ + boolean isInside(int cross) { + if (rule == WIND_NON_ZERO) { + return Crossing.isInsideNonZero(cross); + } + return Crossing.isInsideEvenOdd(cross); + } + + public boolean contains(double px, double py) { + return isInside(Crossing.crossShape(this, px, py)); + } + + public boolean contains(double rx, double ry, double rw, double rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross != Crossing.CROSSING && isInside(cross); + } + + public boolean intersects(double rx, double ry, double rw, double rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross == Crossing.CROSSING || isInside(cross); + } + + public boolean contains(Point2D p) { + return contains(p.getX(), p.getY()); + } + + public boolean contains(Rectangle2D r) { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public boolean intersects(Rectangle2D r) { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public PathIterator getPathIterator(AffineTransform t) { + return new Iterator(this, t); + } + + public PathIterator getPathIterator(AffineTransform t, double flatness) { + return new FlatteningPathIterator(getPathIterator(t), flatness); + } + + @Override + public Object clone() { + try { + GeneralPath p = (GeneralPath)super.clone(); + p.types = types.clone(); + p.points = points.clone(); + return p; + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } + +} |