/* * 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 RoundRectangle2D describes a rectangle with rounded corners with * high-precision data that is appropriate for geometric operations. * * @since Android 1.0 */ public abstract class RoundRectangle2D extends RectangularShape { /** * The Class Float is the subclass of RoundRectangle2D that has all of its * data values stored with float-level precision. * * @since Android 1.0 */ public static class Float extends RoundRectangle2D { /** * The x coordinate of the rectangle's upper left corner. */ public float x; /** * The y coordinate of the rectangle's upper left corner. */ public float y; /** * The width of the rectangle. */ public float width; /** * The height of the rectangle. */ public float height; /** * The arc width of the rounded corners. */ public float arcwidth; /** * The arc height of the rounded corners. */ public float archeight; /** * Instantiates a new float-valued RoundRectangle2D with its data-values * set to zero. */ public Float() { } /** * Instantiates a new float-valued RoundRectangle2D with the specified * data values. * * @param x * the x coordinate of the rectangle's upper left corner. * @param y * the y coordinate of the rectangle's upper left corner. * @param width * the width of the rectangle. * @param height * the height of the rectangle. * @param arcwidth * the arc width of the rounded corners. * @param archeight * the arc height of the rounded corners. */ public Float(float x, float y, float width, float height, float arcwidth, float archeight) { setRoundRect(x, y, width, height, arcwidth, archeight); } @Override public double getX() { return x; } @Override public double getY() { return y; } @Override public double getWidth() { return width; } @Override public double getHeight() { return height; } @Override public double getArcWidth() { return arcwidth; } @Override public double getArcHeight() { return archeight; } @Override public boolean isEmpty() { return width <= 0.0f || height <= 0.0f; } /** * Sets the data of the round rectangle. * * @param x * the x coordinate of the rectangle's upper left corner. * @param y * the y coordinate of the rectangle's upper left corner. * @param width * the width of the rectangle. * @param height * the height of the rectangle. * @param arcwidth * the arc width of the rounded corners. * @param archeight * the arc height of the rounded corners. */ public void setRoundRect(float x, float y, float width, float height, float arcwidth, float archeight) { this.x = x; this.y = y; this.width = width; this.height = height; this.arcwidth = arcwidth; this.archeight = archeight; } @Override public void setRoundRect(double x, double y, double width, double height, double arcwidth, double archeight) { this.x = (float)x; this.y = (float)y; this.width = (float)width; this.height = (float)height; this.arcwidth = (float)arcwidth; this.archeight = (float)archeight; } @Override public void setRoundRect(RoundRectangle2D rr) { this.x = (float)rr.getX(); this.y = (float)rr.getY(); this.width = (float)rr.getWidth(); this.height = (float)rr.getHeight(); this.arcwidth = (float)rr.getArcWidth(); this.archeight = (float)rr.getArcHeight(); } public Rectangle2D getBounds2D() { return new Rectangle2D.Float(x, y, width, height); } } /** * The Class Double is the subclass of RoundRectangle2D that has all of its * data values stored with double-level precision. * * @since Android 1.0 */ public static class Double extends RoundRectangle2D { /** * The x coordinate of the rectangle's upper left corner. */ public double x; /** * The y coordinate of the rectangle's upper left corner. */ public double y; /** * The width of the rectangle. */ public double width; /** * The height of the rectangle. */ public double height; /** * The arc width of the rounded corners. */ public double arcwidth; /** * The arc height of the rounded corners. */ public double archeight; /** * Instantiates a new double-valued RoundRectangle2D with its * data-values set to zero. */ public Double() { } /** * Instantiates a new double-valued RoundRectangle2D with the specified * data values. * * @param x * the x coordinate of the rectangle's upper left corner. * @param y * the y coordinate of the rectangle's upper left corner. * @param width * the width of the rectangle. * @param height * the height of the rectangle. * @param arcwidth * the arc width of the rounded corners. * @param archeight * the arc height of the rounded corners. */ public Double(double x, double y, double width, double height, double arcwidth, double archeight) { setRoundRect(x, y, width, height, arcwidth, archeight); } @Override public double getX() { return x; } @Override public double getY() { return y; } @Override public double getWidth() { return width; } @Override public double getHeight() { return height; } @Override public double getArcWidth() { return arcwidth; } @Override public double getArcHeight() { return archeight; } @Override public boolean isEmpty() { return width <= 0.0 || height <= 0.0; } @Override public void setRoundRect(double x, double y, double width, double height, double arcwidth, double archeight) { this.x = x; this.y = y; this.width = width; this.height = height; this.arcwidth = arcwidth; this.archeight = archeight; } @Override public void setRoundRect(RoundRectangle2D rr) { this.x = rr.getX(); this.y = rr.getY(); this.width = rr.getWidth(); this.height = rr.getHeight(); this.arcwidth = rr.getArcWidth(); this.archeight = rr.getArcHeight(); } public Rectangle2D getBounds2D() { return new Rectangle2D.Double(x, y, width, height); } } /* * RoundRectangle2D path iterator */ /** * The subclass of PathIterator to traverse a RoundRectangle2D. */ class Iterator implements PathIterator { /* * Path for round corners generated the same way as Ellipse2D */ /** * The coefficient to calculate control points of Bezier curves. */ double u = 0.5 - 2.0 / 3.0 * (Math.sqrt(2.0) - 1.0); /** * The points coordinates calculation table. */ double points[][] = { { 0.0, 0.5, 0.0, 0.0 }, // MOVETO { 1.0, -0.5, 0.0, 0.0 }, // LINETO { 1.0, -u, 0.0, 0.0, // CUBICTO 1.0, 0.0, 0.0, u, 1.0, 0.0, 0.0, 0.5 }, { 1.0, 0.0, 1.0, -0.5 }, // LINETO { 1.0, 0.0, 1.0, -u, // CUBICTO 1.0, -u, 1.0, 0.0, 1.0, -0.5, 1.0, 0.0 }, { 0.0, 0.5, 1.0, 0.0 }, // LINETO { 0.0, u, 1.0, 0.0, // CUBICTO 0.0, 0.0, 1.0, -u, 0.0, 0.0, 1.0, -0.5 }, { 0.0, 0.0, 0.0, 0.5 }, // LINETO { 0.0, 0.0, 0.0, u, // CUBICTO 0.0, u, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0 } }; /** * The segment types correspond to points array. */ int types[] = { SEG_MOVETO, SEG_LINETO, SEG_CUBICTO, SEG_LINETO, SEG_CUBICTO, SEG_LINETO, SEG_CUBICTO, SEG_LINETO, SEG_CUBICTO }; /** * The x coordinate of left-upper corner of the round rectangle bounds. */ double x; /** * The y coordinate of left-upper corner of the round rectangle bounds. */ double y; /** * The width of the round rectangle bounds. */ double width; /** * The height of the round rectangle bounds. */ double height; /** * The width of arc corners of the round rectangle. */ double aw; /** * The height of arc corners of the round rectangle. */ double ah; /** * The path iterator transformation. */ AffineTransform t; /** * The current segment index. */ int index; /** * Constructs a new RoundRectangle2D.Iterator for given round rectangle * and transformation. * * @param rr * - the source RoundRectangle2D object * @param at * - the AffineTransform object to apply rectangle path */ Iterator(RoundRectangle2D rr, AffineTransform at) { this.x = rr.getX(); this.y = rr.getY(); this.width = rr.getWidth(); this.height = rr.getHeight(); this.aw = Math.min(width, rr.getArcWidth()); this.ah = Math.min(height, rr.getArcHeight()); this.t = at; if (width < 0.0 || height < 0.0 || aw < 0.0 || ah < 0.0) { index = points.length; } } public int getWindingRule() { return WIND_NON_ZERO; } public boolean isDone() { return index > points.length; } public void next() { index++; } public int currentSegment(double[] coords) { if (isDone()) { // awt.4B=Iterator out of bounds throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ } if (index == points.length) { return SEG_CLOSE; } int j = 0; double p[] = points[index]; for (int i = 0; i < p.length; i += 4) { coords[j++] = x + p[i + 0] * width + p[i + 1] * aw; coords[j++] = y + p[i + 2] * height + p[i + 3] * ah; } if (t != null) { t.transform(coords, 0, coords, 0, j / 2); } return types[index]; } public int currentSegment(float[] coords) { if (isDone()) { // awt.4B=Iterator out of bounds throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ } if (index == points.length) { return SEG_CLOSE; } int j = 0; double p[] = points[index]; for (int i = 0; i < p.length; i += 4) { coords[j++] = (float)(x + p[i + 0] * width + p[i + 1] * aw); coords[j++] = (float)(y + p[i + 2] * height + p[i + 3] * ah); } if (t != null) { t.transform(coords, 0, coords, 0, j / 2); } return types[index]; } } /** * Instantiates a new RoundRectangle2D. */ protected RoundRectangle2D() { } /** * Gets the arc width. * * @return the arc width. */ public abstract double getArcWidth(); /** * Gets the arc height. * * @return the arc height. */ public abstract double getArcHeight(); /** * Sets the data of the RoundRectangle2D. * * @param x * the x coordinate of the rectangle's upper left corner. * @param y * the y coordinate of the rectangle's upper left corner. * @param width * the width of the rectangle. * @param height * the height of the rectangle. * @param arcWidth * the arc width of the rounded corners. * @param arcHeight * the arc height of the rounded corners. */ public abstract void setRoundRect(double x, double y, double width, double height, double arcWidth, double arcHeight); /** * Sets the data of the RoundRectangle2D by copying the values from an * existing RoundRectangle2D. * * @param rr * the round rectangle to copy the data from. * @throws NullPointerException * if rr is null. */ public void setRoundRect(RoundRectangle2D rr) { setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(), rr.getArcWidth(), rr .getArcHeight()); } @Override public void setFrame(double x, double y, double width, double height) { setRoundRect(x, y, width, height, getArcWidth(), getArcHeight()); } public boolean contains(double px, double py) { if (isEmpty()) { return false; } double rx1 = getX(); double ry1 = getY(); double rx2 = rx1 + getWidth(); double ry2 = ry1 + getHeight(); if (px < rx1 || px >= rx2 || py < ry1 || py >= ry2) { return false; } double aw = getArcWidth() / 2.0; double ah = getArcHeight() / 2.0; double cx, cy; if (px < rx1 + aw) { cx = rx1 + aw; } else if (px > rx2 - aw) { cx = rx2 - aw; } else { return true; } if (py < ry1 + ah) { cy = ry1 + ah; } else if (py > ry2 - ah) { cy = ry2 - ah; } else { return true; } px = (px - cx) / aw; py = (py - cy) / ah; return px * px + py * py <= 1.0; } public boolean intersects(double rx, double ry, double rw, double rh) { if (isEmpty() || rw <= 0.0 || rh <= 0.0) { return false; } double x1 = getX(); double y1 = getY(); double x2 = x1 + getWidth(); double y2 = y1 + getHeight(); double rx1 = rx; double ry1 = ry; double rx2 = rx + rw; double ry2 = ry + rh; if (rx2 < x1 || x2 < rx1 || ry2 < y1 || y2 < ry1) { return false; } double cx = (x1 + x2) / 2.0; double cy = (y1 + y2) / 2.0; double nx = cx < rx1 ? rx1 : (cx > rx2 ? rx2 : cx); double ny = cy < ry1 ? ry1 : (cy > ry2 ? ry2 : cy); return contains(nx, ny); } public boolean contains(double rx, double ry, double rw, double rh) { if (isEmpty() || rw <= 0.0 || rh <= 0.0) { return false; } double rx1 = rx; double ry1 = ry; double rx2 = rx + rw; double ry2 = ry + rh; return contains(rx1, ry1) && contains(rx2, ry1) && contains(rx2, ry2) && contains(rx1, ry2); } public PathIterator getPathIterator(AffineTransform at) { return new Iterator(this, at); } }