13381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard/* 23381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * Copyright (C) 2016 The Android Open Source Project 33381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * 43381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * Licensed under the Apache License, Version 2.0 (the "License"); 53381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * you may not use this file except in compliance with the License. 63381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * You may obtain a copy of the License at 73381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * 83381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * http://www.apache.org/licenses/LICENSE-2.0 93381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * 103381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * Unless required by applicable law or agreed to in writing, software 113381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * distributed under the License is distributed on an "AS IS" BASIS, 123381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * See the License for the specific language governing permissions and 143381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * limitations under the License. 153381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard */ 163381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 173381cde9f293c52f195b31b0e4049649db31181aJerome Gaillardpackage android.graphics; 183381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 193381cde9f293c52f195b31b0e4049649db31181aJerome Gaillardimport java.awt.geom.AffineTransform; 203381cde9f293c52f195b31b0e4049649db31181aJerome Gaillardimport java.awt.geom.PathIterator; 213381cde9f293c52f195b31b0e4049649db31181aJerome Gaillardimport java.awt.geom.Rectangle2D; 223381cde9f293c52f195b31b0e4049649db31181aJerome Gaillardimport java.awt.geom.RectangularShape; 233381cde9f293c52f195b31b0e4049649db31181aJerome Gaillardimport java.awt.geom.RoundRectangle2D; 243381cde9f293c52f195b31b0e4049649db31181aJerome Gaillardimport java.util.EnumSet; 253381cde9f293c52f195b31b0e4049649db31181aJerome Gaillardimport java.util.NoSuchElementException; 263381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 273381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard/** 283381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * Defines a rectangle with rounded corners, where the sizes of the corners 293381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * are potentially different. 303381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard */ 313381cde9f293c52f195b31b0e4049649db31181aJerome Gaillardpublic class RoundRectangle extends RectangularShape { 323381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double x; 333381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double y; 343381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double width; 353381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double height; 363381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double ulWidth; 373381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double ulHeight; 383381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double urWidth; 393381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double urHeight; 403381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double lrWidth; 413381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double lrHeight; 423381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double llWidth; 433381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double llHeight; 443381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 453381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard private enum Zone { 463381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard CLOSE_OUTSIDE, 473381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard CLOSE_INSIDE, 483381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard MIDDLE, 493381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard FAR_INSIDE, 503381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard FAR_OUTSIDE 513381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 523381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 533381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard private final EnumSet<Zone> close = EnumSet.of(Zone.CLOSE_OUTSIDE, Zone.CLOSE_INSIDE); 543381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard private final EnumSet<Zone> far = EnumSet.of(Zone.FAR_OUTSIDE, Zone.FAR_INSIDE); 553381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 563381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard /** 573381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * @param cornerDimensions array of 8 floating-point number corresponding to the width and 583381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * the height of each corner in the following order: upper-left, upper-right, lower-right, 593381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * lower-left. It assumes for the size the same convention as {@link RoundRectangle2D}, that 603381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * is that the width and height of a corner correspond to the total width and height of the 613381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard * ellipse that corner is a quarter of. 623381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard */ 633381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public RoundRectangle(float x, float y, float width, float height, float[] cornerDimensions) { 643381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (cornerDimensions.length != 8) { 653381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard throw new IllegalArgumentException("The array of corner dimensions must have eight " + 663381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard "elements"); 673381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 683381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 693381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.x = x; 703381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.y = y; 713381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.width = width; 723381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.height = height; 733381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 743381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard float[] dimensions = cornerDimensions.clone(); 753381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // If a value is negative, the corresponding corner is squared 763381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard for (int i = 0; i < dimensions.length; i += 2) { 773381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (dimensions[i] < 0 || dimensions[i + 1] < 0) { 783381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard dimensions[i] = 0; 793381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard dimensions[i + 1] = 0; 803381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 813381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 823381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 833381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double topCornerWidth = (dimensions[0] + dimensions[2]) / 2d; 843381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double bottomCornerWidth = (dimensions[4] + dimensions[6]) / 2d; 853381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double leftCornerHeight = (dimensions[1] + dimensions[7]) / 2d; 863381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double rightCornerHeight = (dimensions[3] + dimensions[5]) / 2d; 873381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 883381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Rescale the corner dimensions if they are bigger than the rectangle 893381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double scale = Math.min(1.0, width / topCornerWidth); 903381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard scale = Math.min(scale, width / bottomCornerWidth); 913381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard scale = Math.min(scale, height / leftCornerHeight); 923381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard scale = Math.min(scale, height / rightCornerHeight); 933381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 943381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.ulWidth = dimensions[0] * scale; 953381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.ulHeight = dimensions[1] * scale; 963381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.urWidth = dimensions[2] * scale; 973381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.urHeight = dimensions[3] * scale; 983381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.lrWidth = dimensions[4] * scale; 993381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.lrHeight = dimensions[5] * scale; 1003381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.llWidth = dimensions[6] * scale; 1013381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.llHeight = dimensions[7] * scale; 1023381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1033381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1043381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 1053381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double getX() { 1063381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return x; 1073381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1083381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1093381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 1103381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double getY() { 1113381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return y; 1123381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1133381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1143381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 1153381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double getWidth() { 1163381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return width; 1173381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1183381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1193381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 1203381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public double getHeight() { 1213381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return height; 1223381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1233381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1243381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 1253381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public boolean isEmpty() { 1263381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return (width <= 0d) || (height <= 0d); 1273381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1283381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1293381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 1303381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public void setFrame(double x, double y, double w, double h) { 1313381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.x = x; 1323381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.y = y; 1333381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.width = w; 1343381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard this.height = h; 1353381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1363381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1373381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 1383381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public Rectangle2D getBounds2D() { 1393381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return new Rectangle2D.Double(x, y, width, height); 1403381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1413381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1423381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 1433381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public boolean contains(double x, double y) { 1443381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (isEmpty()) { 1453381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return false; 1463381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1473381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1483381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double x0 = getX(); 1493381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double y0 = getY(); 1503381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double x1 = x0 + getWidth(); 1513381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double y1 = y0 + getHeight(); 1523381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Check for trivial rejection - point is outside bounding rectangle 1533381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (x < x0 || y < y0 || x >= x1 || y >= y1) { 1543381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return false; 1553381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1563381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1573381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double insideTopX0 = x0 + ulWidth / 2d; 1583381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double insideLeftY0 = y0 + ulHeight / 2d; 1593381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (x < insideTopX0 && y < insideLeftY0) { 1603381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // In the upper-left corner 1613381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return isInsideCorner(x - insideTopX0, y - insideLeftY0, ulWidth / 2d, ulHeight / 2d); 1623381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1633381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1643381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double insideTopX1 = x1 - urWidth / 2d; 1653381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double insideRightY0 = y0 + urHeight / 2d; 1663381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (x > insideTopX1 && y < insideRightY0) { 1673381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // In the upper-right corner 1683381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return isInsideCorner(x - insideTopX1, y - insideRightY0, urWidth / 2d, urHeight / 2d); 1693381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1703381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1713381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double insideBottomX1 = x1 - lrWidth / 2d; 1723381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double insideRightY1 = y1 - lrHeight / 2d; 1733381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (x > insideBottomX1 && y > insideRightY1) { 1743381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // In the lower-right corner 1753381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return isInsideCorner(x - insideBottomX1, y - insideRightY1, lrWidth / 2d, 1763381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard lrHeight / 2d); 1773381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1783381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1793381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double insideBottomX0 = x0 + llWidth / 2d; 1803381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double insideLeftY1 = y1 - llHeight / 2d; 1813381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (x < insideBottomX0 && y > insideLeftY1) { 1823381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // In the lower-left corner 1833381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return isInsideCorner(x - insideBottomX0, y - insideLeftY1, llWidth / 2d, 1843381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard llHeight / 2d); 1853381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1863381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1873381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // In the central part of the rectangle 1883381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return true; 1893381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1903381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1913381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard private boolean isInsideCorner(double x, double y, double width, double height) { 1923381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double squareDist = height * height * x * x + width * width * y * y; 1933381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return squareDist <= width * width * height * height; 1943381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 1953381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1963381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard private Zone classify(double coord, double side1, double arcSize1, double side2, 1973381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double arcSize2) { 1983381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (coord < side1) { 1993381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return Zone.CLOSE_OUTSIDE; 2003381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } else if (coord < side1 + arcSize1) { 2013381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return Zone.CLOSE_INSIDE; 2023381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } else if (coord < side2 - arcSize2) { 2033381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return Zone.MIDDLE; 2043381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } else if (coord < side2) { 2053381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return Zone.FAR_INSIDE; 2063381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } else { 2073381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return Zone.FAR_OUTSIDE; 2083381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2093381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2103381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 2113381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public boolean intersects(double x, double y, double w, double h) { 2123381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (isEmpty() || w <= 0 || h <= 0) { 2133381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return false; 2143381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2153381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double x0 = getX(); 2163381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double y0 = getY(); 2173381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double x1 = x0 + getWidth(); 2183381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double y1 = y0 + getHeight(); 2193381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Check for trivial rejection - bounding rectangles do not intersect 2203381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (x + w <= x0 || x >= x1 || y + h <= y0 || y >= y1) { 2213381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return false; 2223381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2233381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 2243381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double maxLeftCornerWidth = Math.max(ulWidth, llWidth) / 2d; 2253381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double maxRightCornerWidth = Math.max(urWidth, lrWidth) / 2d; 2263381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double maxUpperCornerHeight = Math.max(ulHeight, urHeight) / 2d; 2273381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double maxLowerCornerHeight = Math.max(llHeight, lrHeight) / 2d; 2283381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard Zone x0class = classify(x, x0, maxLeftCornerWidth, x1, maxRightCornerWidth); 2293381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard Zone x1class = classify(x + w, x0, maxLeftCornerWidth, x1, maxRightCornerWidth); 2303381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard Zone y0class = classify(y, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight); 2313381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard Zone y1class = classify(y + h, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight); 2323381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 2333381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Trivially accept if any point is inside inner rectangle 2343381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (x0class == Zone.MIDDLE || x1class == Zone.MIDDLE || y0class == Zone.MIDDLE || y1class == Zone.MIDDLE) { 2353381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return true; 2363381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2373381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Trivially accept if either edge spans inner rectangle 2383381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if ((close.contains(x0class) && far.contains(x1class)) || (close.contains(y0class) && 2393381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard far.contains(y1class))) { 2403381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return true; 2413381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2423381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 2433381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Since neither edge spans the center, then one of the corners 2443381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // must be in one of the rounded edges. We detect this case if 2453381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // a [xy]0class is 3 or a [xy]1class is 1. One of those two cases 2463381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // must be true for each direction. 2473381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // We now find a "nearest point" to test for being inside a rounded 2483381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // corner. 2493381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (x1class == Zone.CLOSE_INSIDE && y1class == Zone.CLOSE_INSIDE) { 2503381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Potentially in upper-left corner 2513381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard x = x + w - x0 - ulWidth / 2d; 2523381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard y = y + h - y0 - ulHeight / 2d; 2533381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return x > 0 || y > 0 || isInsideCorner(x, y, ulWidth / 2d, ulHeight / 2d); 2543381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2553381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (x1class == Zone.CLOSE_INSIDE) { 2563381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Potentially in lower-left corner 2573381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard x = x + w - x0 - llWidth / 2d; 2583381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard y = y - y1 + llHeight / 2d; 2593381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return x > 0 || y < 0 || isInsideCorner(x, y, llWidth / 2d, llHeight / 2d); 2603381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2613381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (y1class == Zone.CLOSE_INSIDE) { 2623381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard //Potentially in the upper-right corner 2633381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard x = x - x1 + urWidth / 2d; 2643381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard y = y + h - y0 - urHeight / 2d; 2653381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return x < 0 || y > 0 || isInsideCorner(x, y, urWidth / 2d, urHeight / 2d); 2663381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2673381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Potentially in the lower-right corner 2683381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard x = x - x1 + lrWidth / 2d; 2693381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard y = y - y1 + lrHeight / 2d; 2703381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return x < 0 || y < 0 || isInsideCorner(x, y, lrWidth / 2d, lrHeight / 2d); 2713381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2723381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 2733381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 2743381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public boolean contains(double x, double y, double w, double h) { 2753381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (isEmpty() || w <= 0 || h <= 0) { 2763381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return false; 2773381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2783381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return (contains(x, y) && 2793381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard contains(x + w, y) && 2803381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard contains(x, y + h) && 2813381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard contains(x + w, y + h)); 2823381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 2833381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 2843381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 2853381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public PathIterator getPathIterator(final AffineTransform at) { 2863381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return new PathIterator() { 2873381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard int index; 2883381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 2893381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // ArcIterator.btan(Math.PI/2) 2903381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public static final double CtrlVal = 0.5522847498307933; 2913381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard private final double ncv = 1.0 - CtrlVal; 2923381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 2933381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // Coordinates of control points for Bezier curves approximating the straight lines 2943381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard // and corners of the rounded rectangle. 2953381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard private final double[][] ctrlpts = { 2963381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {0.0, 0.0, 0.0, ulHeight}, 2973381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {0.0, 0.0, 1.0, -llHeight}, 2983381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {0.0, 0.0, 1.0, -llHeight * ncv, 0.0, ncv * llWidth, 1.0, 0.0, 0.0, llWidth, 2993381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 1.0, 0.0}, 3003381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {1.0, -lrWidth, 1.0, 0.0}, 3013381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {1.0, -lrWidth * ncv, 1.0, 0.0, 1.0, 0.0, 1.0, -lrHeight * ncv, 1.0, 0.0, 1.0, 3023381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard -lrHeight}, 3033381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {1.0, 0.0, 0.0, urHeight}, 3043381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {1.0, 0.0, 0.0, ncv * urHeight, 1.0, -urWidth * ncv, 0.0, 0.0, 1.0, -urWidth, 3053381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 0.0, 0.0}, 3063381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {0.0, ulWidth, 0.0, 0.0}, 3073381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {0.0, ncv * ulWidth, 0.0, 0.0, 0.0, 0.0, 0.0, ncv * ulHeight, 0.0, 0.0, 0.0, 3083381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard ulHeight}, 3093381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard {} 3103381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard }; 3113381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard private final int[] types = { 3123381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard SEG_MOVETO, 3133381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard SEG_LINETO, SEG_CUBICTO, 3143381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard SEG_LINETO, SEG_CUBICTO, 3153381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard SEG_LINETO, SEG_CUBICTO, 3163381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard SEG_LINETO, SEG_CUBICTO, 3173381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard SEG_CLOSE, 3183381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard }; 3193381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 3203381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 3213381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public int getWindingRule() { 3223381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return WIND_NON_ZERO; 3233381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3243381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 3253381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 3263381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public boolean isDone() { 3273381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return index >= ctrlpts.length; 3283381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3293381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 3303381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 3313381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public void next() { 3323381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard index++; 3333381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3343381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 3353381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 3363381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public int currentSegment(float[] coords) { 3373381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (isDone()) { 3383381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard throw new NoSuchElementException("roundrect iterator out of bounds"); 3393381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3403381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard int nc = 0; 3413381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double ctrls[] = ctrlpts[index]; 3423381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard for (int i = 0; i < ctrls.length; i += 4) { 3433381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard coords[nc++] = (float) (x + ctrls[i] * width + ctrls[i + 1] / 2d); 3443381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard coords[nc++] = (float) (y + ctrls[i + 2] * height + ctrls[i + 3] / 2d); 3453381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3463381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (at != null) { 3473381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard at.transform(coords, 0, coords, 0, nc / 2); 3483381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3493381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return types[index]; 3503381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3513381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard 3523381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard @Override 3533381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard public int currentSegment(double[] coords) { 3543381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (isDone()) { 3553381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard throw new NoSuchElementException("roundrect iterator out of bounds"); 3563381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3573381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard int nc = 0; 3583381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard double ctrls[] = ctrlpts[index]; 3593381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard for (int i = 0; i < ctrls.length; i += 4) { 3603381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard coords[nc++] = x + ctrls[i] * width + ctrls[i + 1] / 2d; 3613381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard coords[nc++] = y + ctrls[i + 2] * height + ctrls[i + 3] / 2d; 3623381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3633381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard if (at != null) { 3643381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard at.transform(coords, 0, coords, 0, nc / 2); 3653381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3663381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard return types[index]; 3673381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3683381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard }; 3693381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard } 3703381cde9f293c52f195b31b0e4049649db31181aJerome Gaillard} 371