/* * $Id$ * This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc * * Copyright (c) 2000-2012 Stephane GALLAND. * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports, * Universite de Technologie de Belfort-Montbeliard. * Copyright (c) 2013-2016 The original authors, and other authors. * * Licensed 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. */ package org.arakhne.afc.math.geometry.d2.afp; import java.util.NoSuchElementException; import org.eclipse.xtext.xbase.lib.Pure; import org.arakhne.afc.math.MathConstants; import org.arakhne.afc.math.MathUtil; import org.arakhne.afc.math.Unefficient; import org.arakhne.afc.math.geometry.CrossingComputationType; import org.arakhne.afc.math.geometry.PathWindingRule; import org.arakhne.afc.math.geometry.d2.Point2D; import org.arakhne.afc.math.geometry.d2.Transform2D; import org.arakhne.afc.math.geometry.d2.Vector2D; import org.arakhne.afc.math.geometry.d2.afp.Circle2afp.AbstractCirclePathIterator; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** Fonctional interface that represented a 2D round rectangle on a plane. * * @param <ST> is the type of the general implementation. * @param <IT> is the type of the implementation of this shape. * @param <IE> is the type of the path elements. * @param <P> is the type of the points. * @param <V> is the type of the vectors. * @param <B> is the type of the bounding boxes. * @author $Author: sgalland$ * @author $Author: hjaffali$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public interface RoundRectangle2afp< ST extends Shape2afp<?, ?, IE, P, V, B>, IT extends RoundRectangle2afp<?, ?, IE, P, V, B>, IE extends PathElement2afp, P extends Point2D<? super P, ? super V>, V extends Vector2D<? super V, ? super P>, B extends Rectangle2afp<?, ?, IE, P, V, B>> extends RectangularShape2afp<ST, IT, IE, P, V, B> { /** Replies if a rectangle is inside in the round rectangle. * * @param rx1 is the lowest corner of the round rectangle. * @param ry1 is the lowest corner of the round rectangle. * @param rwidth1 is the width of the round rectangle. * @param rheight1 is the height of the round rectangle. * @param awidth is the width of the arc of the round rectangle. * @param aheight is the height of the arc of the round rectangle. * @param rx2 is the lowest corner of the inner-candidate rectangle. * @param ry2 is the lowest corner of the inner-candidate rectangle. * @param rwidth2 is the width of the inner-candidate rectangle. * @param rheight2 is the height of the inner-candidate rectangle. * @return <code>true</code> if the given rectangle is inside the ellipse; * otherwise <code>false</code>. */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean containsRoundRectangleRectangle(double rx1, double ry1, double rwidth1, double rheight1, double awidth, double aheight, double rx2, double ry2, double rwidth2, double rheight2) { assert rwidth1 >= 0. : AssertMessages.positiveOrZeroParameter(2); assert rheight1 >= 0. : AssertMessages.positiveOrZeroParameter(3); assert awidth >= 0. : AssertMessages.positiveOrZeroParameter(4); assert aheight >= 0. : AssertMessages.positiveOrZeroParameter(5); assert rwidth2 >= 0. : AssertMessages.positiveOrZeroParameter(8); assert rheight2 >= 0. : AssertMessages.positiveOrZeroParameter(9); final double rcx1 = rx1 + rwidth1 / 2; final double rcy1 = ry1 + rheight1 / 2; final double rcx2 = rx2 + rwidth2 / 2; final double rcy2 = ry2 + rheight2 / 2; final double farX; if (rcx1 <= rcx2) { farX = rx2 + rwidth2; } else { farX = rx2; } final double farY; if (rcy1 <= rcy2) { farY = ry2 + rheight2; } else { farY = ry2; } return containsRoundRectanglePoint(rx1, ry1, rwidth1, rheight1, awidth, aheight, farX, farY); } /** Replies if a point is inside in the round rectangle. * * @param rx is the lowest corner of the round rectangle. * @param ry is the lowest corner of the round rectangle. * @param rwidth is the width of the round rectangle. * @param rheight is the height of the round rectangle. * @param awidth is the width of the arc of the round rectangle. * @param aheight is the height of the arc of the round rectangle. * @param px is the point. * @param py is the point. * @return <code>true</code> if the given rectangle is inside the ellipse; * otherwise <code>false</code>. */ @Pure @SuppressWarnings("checkstyle:magicnumber") static boolean containsRoundRectanglePoint(double rx, double ry, double rwidth, double rheight, double awidth, double aheight, double px, double py) { assert rwidth >= 0. : AssertMessages.positiveOrZeroParameter(2); assert rheight >= 0. : AssertMessages.positiveOrZeroParameter(3); assert awidth >= 0. : AssertMessages.positiveOrZeroParameter(4); assert aheight >= 0. : AssertMessages.positiveOrZeroParameter(5); if (rwidth <= 0 && rheight <= 0) { return rx == px && ry == py; } final double rrx = rx + rwidth; final double rry = ry + rheight; // Check for trivial rejection - point is outside bounding rectangle if (px < rx || py < ry || px > rrx || py > rry) { return false; } final double aw = Math.min(rwidth, Math.abs(awidth)) / 2.; final double ah = Math.min(rheight, Math.abs(aheight)) / 2.; // Check which corner point is in and do circular containment // test - otherwise simple acceptance final double irx = rx + aw; final double bx; if (px < irx) { bx = irx; } else { final double irrx = rrx - aw; if (px > irrx) { bx = irrx; } else { return true; } } final double iry = ry + ah; final double by; if (py < iry) { by = iry; } else { final double irry = rry - ah; if (py > irry) { by = irry; } else { return true; } } final double xx = (px - bx) / aw; final double yy = (py - by) / ah; return xx * xx + yy * yy <= 1.; } /** Replies if the round rectangle is intersecting the segment. * * @param rx1 is the first corner of the rectangle. * @param ry1 is the first corner of the rectangle. * @param rx2 is the second corner of the rectangle. * @param ry2 is the second corner of the rectangle. * @param aw is the width of the arcs of the rectangle. * @param ah is the height of the arcs of the rectangle. * @param sx1 is the first point of the segment. * @param sy1 is the first point of the segment. * @param sx2 is the second point of the segment. * @param sy2 is the second point of the segment. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity", "checkstyle:nestedifdepth", "checkstyle:magicnumber"}) static boolean intersectsRoundRectangleSegment(double rx1, double ry1, double rx2, double ry2, double aw, double ah, double sx1, double sy1, double sx2, double sy2) { assert rx1 <= rx2 : AssertMessages.lowerEqualParameters(0, rx1, 2, rx2); assert ry1 <= ry2 : AssertMessages.lowerEqualParameters(1, ry1, 3, ry2); assert aw >= 0. : AssertMessages.positiveOrZeroParameter(4); assert ah >= 0. : AssertMessages.positiveOrZeroParameter(5); double segmentX1 = sx1; double segmentY1 = sy1; double segmentX2 = sx2; double segmentY2 = sy2; int code1 = MathUtil.getCohenSutherlandCode(segmentX1, segmentY1, rx1, ry1, rx2, ry2); int code2 = MathUtil.getCohenSutherlandCode(segmentX2, segmentY2, rx1, ry1, rx2, ry2); while (true) { if ((code1 | code2) == 0) { // Bitwise OR is 0. Trivially accept and get out of loop // Special case: if the segment is empty, it is on the border => no intersection. if (segmentX1 != segmentX2 || segmentY1 != segmentY2) { // Special case: intersecting outside the corner's ellipse. final double ellipseMinX = rx1 + aw; if (segmentX1 <= ellipseMinX && segmentX2 <= ellipseMinX) { final double ellipseMinY = ry1 + ah; if (segmentY1 <= ellipseMinY && segmentY2 <= ellipseMinY) { return Ellipse2afp.intersectsEllipseSegment( rx1, ry1, aw * 2, ah * 2, sx1, sy1, sx2, sy2, false); } final double ellipseMaxY = ry2 - ah; if (segmentY1 >= ellipseMaxY && segmentY2 >= ellipseMaxY) { final double ellipseHeight = ah * 2; return Ellipse2afp.intersectsEllipseSegment( rx1, ry1 - ellipseHeight, aw * 2, ellipseHeight, sx1, sy1, sx2, sy2, false); } } else { final double ellipseMaxX = rx2 - aw; if (segmentX1 >= ellipseMaxX && segmentX2 >= ellipseMaxX) { final double ellipseMinY = ry1 + ah; final double ellipseWidth = aw * 2; if (segmentY1 <= ellipseMinY && segmentY2 <= ellipseMinY) { return Ellipse2afp.intersectsEllipseSegment( rx2 - ellipseWidth, ry1, ellipseWidth, ah * 2, sx1, sy1, sx2, sy2, false); } final double ellipseMaxY = ry2 - ah; if (segmentY1 >= ellipseMaxY && segmentY2 >= ellipseMaxY) { final double ellipseHeight = ah * 2; return Ellipse2afp.intersectsEllipseSegment( rx2 - ellipseWidth, ry2 - ellipseHeight, ellipseWidth, ellipseHeight, sx1, sy1, sx2, sy2, false); } } } return true; } return false; } if ((code1 & code2) != 0) { // Bitwise AND is not 0. Trivially reject and get out of loop return false; } // failed both tests, so calculate the line segment intersection // At least one endpoint is outside the clip rectangle; pick it. int code3 = (code1 != 0) ? code1 : code2; double x = 0; double y = 0; // Now find the intersection point; // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0) if ((code3 & MathConstants.COHEN_SUTHERLAND_TOP) != 0) { // point is above the clip rectangle x = segmentX1 + (segmentX2 - segmentX1) * (ry2 - segmentY1) / (segmentY2 - segmentY1); y = ry2; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) { // point is below the clip rectangle x = segmentX1 + (segmentX2 - segmentX1) * (ry1 - segmentY1) / (segmentY2 - segmentY1); y = ry1; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) { // point is to the right of clip rectangle y = segmentY1 + (segmentY2 - segmentY1) * (rx2 - segmentX1) / (segmentX2 - segmentX1); x = rx2; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) { // point is to the left of clip rectangle y = segmentY1 + (segmentY2 - segmentY1) * (rx1 - segmentX1) / (segmentX2 - segmentX1); x = rx1; } else { code3 = 0; } if (code3 != 0) { // Now we move outside point to intersection point to clip // and get ready for next pass. if (code3 == code1) { segmentX1 = x; segmentY1 = y; code1 = MathUtil.getCohenSutherlandCode(segmentX1, segmentY1, rx1, ry1, rx2, ry2); } else { segmentX2 = x; segmentY2 = y; code2 = MathUtil.getCohenSutherlandCode(segmentX2, segmentY2, rx1, ry1, rx2, ry2); } } } } /** Replies if two round rectangles are intersecting. * * @param r1x1 is the first corner of the first rectangle. * @param r1y1 is the first corner of the first rectangle. * @param r1x2 is the second corner of the first rectangle. * @param r1y2 is the second corner of the first rectangle. * @param r1aw is the width of the arcs of the first rectangle. * @param r1ah is the height of the arcs of the first rectangle. * @param r2x1 is the first corner of the second rectangle. * @param r2y1 is the first corner of the second rectangle. * @param r2x2 is the second corner of the second rectangle. * @param r2y2 is the second corner of the second rectangle. * @param r2aw is the width of the arcs of the second rectangle. * @param r2ah is the height of the arcs of the second rectangle. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @Unefficient @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean intersectsRoundRectangleRoundRectangle(double r1x1, double r1y1, double r1x2, double r1y2, double r1aw, double r1ah, double r2x1, double r2y1, double r2x2, double r2y2, double r2aw, double r2ah) { assert r1x1 <= r1x2 : AssertMessages.lowerEqualParameters(0, r1x1, 2, r1x2); assert r1y1 <= r1y2 : AssertMessages.lowerEqualParameters(1, r1y1, 3, r1y2); assert r1aw >= 0. : AssertMessages.positiveOrZeroParameter(4); assert r1ah >= 0. : AssertMessages.positiveOrZeroParameter(5); assert r2x1 <= r2x2 : AssertMessages.lowerEqualParameters(6, r2x1, 8, r2x2); assert r2y1 <= r2y2 : AssertMessages.lowerEqualParameters(7, r2y1, 9, r2y2); assert r2aw >= 0. : AssertMessages.positiveOrZeroParameter(10); assert r2ah >= 0. : AssertMessages.positiveOrZeroParameter(11); if (Rectangle2afp.intersectsRectangleRectangle(r1x1, r1y1, r1x2, r1y2, r2x1, r2y1, r2x2, r2y2)) { // Boundings rectangles are intersecting // Test internal rectangles final double r1InnerMinX = r1x1 + r1aw; final double r1InnerMaxX = r1x2 - r1aw; final double r1InnerMinY = r1y1 + r1ah; final double r1InnerMaxY = r1y2 - r1ah; final double r2InnerMinX = r2x1 + r2aw; final double r2InnerMaxX = r2x2 - r2aw; final double r2InnerMinY = r2y1 + r2ah; final double r2InnerMaxY = r2y2 - r2ah; if (Rectangle2afp.intersectsRectangleRectangle( r1InnerMinX, r1y1, r1InnerMaxX, r1y2, r2InnerMinX, r2y1, r2InnerMaxX, r2y2) || Rectangle2afp.intersectsRectangleRectangle( r1InnerMinX, r1y1, r1InnerMaxX, r1y2, r2x1, r2InnerMinY, r2x2, r2InnerMaxY) || Rectangle2afp.intersectsRectangleRectangle( r1x1, r1InnerMinY, r1x2, r1InnerMaxY, r2InnerMinX, r2y1, r2InnerMaxX, r2y2) || Rectangle2afp.intersectsRectangleRectangle( r1x1, r1InnerMinY, r1x2, r1InnerMaxY, r2x1, r2InnerMinY, r2x2, r2InnerMaxY)) { return true; } // Test closest corner ellipses final double ex1; final double ey1; final double ex2; final double ey2; if (r1InnerMaxX <= r2x1) { ex1 = r1InnerMaxX - r1aw; ex2 = r2x1; } else { ex1 = r1x1; ex2 = r2InnerMaxX - r2aw; } if (r1InnerMaxY < r2y1) { ey1 = r1InnerMaxY - r1ah; ey2 = r2y1; } else { ey1 = r1y1; ey2 = r2InnerMaxY - r2ah; } return Ellipse2afp.intersectsEllipseEllipse( ex1, ey1, r1aw * 2, r1ah * 2, ex2, ey2, r2aw * 2, r2ah * 2); } return false; } /** Replies if a round rectangle and a circle are intersecting. * * @param rx1 is the first corner of the rectangle. * @param ry1 is the first corner of the rectangle. * @param rx2 is the second corner of the rectangle. * @param ry2 is the second corner of the rectangle. * @param aw is the width of the arcs of the rectangle. * @param ah is the height of the arcs of the rectangle. * @param cx is the center of the circle. * @param cy is the center of the circle. * @param radius is the radius of the circle. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @Unefficient @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean intersectsRoundRectangleCircle(double rx1, double ry1, double rx2, double ry2, double aw, double ah, double cx, double cy, double radius) { assert rx1 <= rx2 : AssertMessages.lowerEqualParameters(0, rx1, 2, rx2); assert ry1 <= ry2 : AssertMessages.lowerEqualParameters(1, ry1, 3, ry2); assert aw >= 0. : AssertMessages.positiveOrZeroParameter(4); assert ah >= 0. : AssertMessages.positiveOrZeroParameter(5); assert radius >= 0. : AssertMessages.positiveOrZeroParameter(8); final double rInnerMinX = rx1 + aw; final double rInnerMaxX = rx2 - aw; final double rInnerMinY = ry1 + ah; final double rInnerMaxY = ry2 - ah; if (cx < rInnerMinX) { if (cy < rInnerMinY) { return Ellipse2afp.intersectsEllipseCircle(rx1, ry1, aw * 2, ah * 2, cx, cy, radius); } if (cy > rInnerMaxY) { return Ellipse2afp.intersectsEllipseCircle(rx1, rInnerMaxY - ah, aw * 2, ah * 2, cx, cy, radius); } return cx > (rx1 - radius) && cx < (rx2 + radius); } if (cx > rInnerMaxX) { if (cy < ry1) { return Ellipse2afp.intersectsEllipseCircle(rInnerMaxX - aw, ry1, aw * 2, ah * 2, cx, cy, radius); } if (cy > rInnerMaxY) { return Ellipse2afp.intersectsEllipseCircle(rInnerMaxX - aw, rInnerMaxY - ah, aw * 2, ah * 2, cx, cy, radius); } return cx > (rx1 - radius) && cx < (rx2 + radius); } return cy > (ry1 - radius) && cy < (ry2 + radius); } /** Replies if a round rectangle and a rectangle are intersecting. * * @param r1x1 is the first corner of the first rectangle. * @param r1y1 is the first corner of the first rectangle. * @param r1x2 is the second corner of the first rectangle. * @param r1y2 is the second corner of the first rectangle. * @param r1aw is the width of the arcs of the first rectangle. * @param r1ah is the height of the arcs of the first rectangle. * @param r2x1 is the first corner of the second rectangle. * @param r2y1 is the first corner of the second rectangle. * @param r2x2 is the second corner of the second rectangle. * @param r2y2 is the second corner of the second rectangle. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @Unefficient @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean intersectsRoundRectangleRectangle(double r1x1, double r1y1, double r1x2, double r1y2, double r1aw, double r1ah, double r2x1, double r2y1, double r2x2, double r2y2) { assert r1x1 <= r1x2 : AssertMessages.lowerEqualParameters(0, r1x1, 2, r1x2); assert r1y1 <= r1y2 : AssertMessages.lowerEqualParameters(1, r1y1, 3, r1y2); assert r1aw >= 0. : AssertMessages.positiveOrZeroParameter(4); assert r1ah >= 0. : AssertMessages.positiveOrZeroParameter(5); assert r2x1 <= r2x2 : AssertMessages.lowerEqualParameters(6, r2x1, 8, r2x2); assert r2y1 <= r2y2 : AssertMessages.lowerEqualParameters(7, r2y1, 9, r2y2); if (Rectangle2afp.intersectsRectangleRectangle(r1x1, r1y1, r1x2, r1y2, r2x1, r2y1, r2x2, r2y2)) { // Boundings rectangles are intersecting // Test internal rectangles final double r1InnerMinX = r1x1 + r1aw; final double r1InnerMaxX = r1x2 - r1aw; final double r1InnerMinY = r1y1 + r1ah; final double r1InnerMaxY = r1y2 - r1ah; if (Rectangle2afp.intersectsRectangleRectangle( r1InnerMinX, r1y1, r1InnerMaxX, r1y2, r2x1, r2y1, r2x2, r2y2) || Rectangle2afp.intersectsRectangleRectangle( r1x1, r1InnerMinY, r1x2, r1InnerMaxY, r2x1, r2y1, r2x2, r2y2)) { return true; } // Test closest corner ellipses final double ex1; final double ey1; if (r1InnerMaxX <= r2x1) { ex1 = r1InnerMaxX - r1aw; } else { ex1 = r1x1; } if (r1InnerMaxY < r2y1) { ey1 = r1InnerMaxY - r1ah; } else { ey1 = r1y1; } return Ellipse2afp.intersectsEllipseRectangle( ex1, ey1, r1aw * 2, r1ah * 2, r2x1, r2y1, r2x2, r2y2); } return false; } /** Replies if a round rectangle and an ellipse are intersecting. * * @param rx1 is the first corner of the rectangle. * @param ry1 is the first corner of the rectangle. * @param rx2 is the second corner of the rectangle. * @param ry2 is the second corner of the rectangle. * @param aw is the width of the arcs of the rectangle. * @param ah is the height of the arcs of the rectangle. * @param ex is the coordinate of the ellipse corner. * @param ey is the coordinate of the ellipse corner. * @param ew is the width of the ellipse. * @param eh is the height of the ellipse. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @Unefficient @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean intersectsRoundRectangleEllipse(double rx1, double ry1, double rx2, double ry2, double aw, double ah, double ex, double ey, double ew, double eh) { assert rx1 <= rx2 : AssertMessages.lowerEqualParameters(0, rx1, 2, rx2); assert ry1 <= ry2 : AssertMessages.lowerEqualParameters(1, ry1, 3, ry2); assert aw >= 0. : AssertMessages.positiveOrZeroParameter(2); assert ah >= 0. : AssertMessages.positiveOrZeroParameter(5); assert ew >= 0. : AssertMessages.positiveOrZeroParameter(8); assert eh >= 0. : AssertMessages.positiveOrZeroParameter(9); final double rInnerMinX = rx1 + aw; final double rInnerMaxX = rx2 - aw; final double rInnerMinY = ry1 + ah; final double rInnerMaxY = ry2 - ah; final double radius1 = ew / 2; final double radius2 = eh / 2; final double centerX = ex + radius1; final double centerY = ey + radius2; if (centerX < rInnerMinX) { if (centerY < rInnerMinY) { return Ellipse2afp.intersectsEllipseEllipse(rx1, ry1, aw * 2, ah * 2, ex, ey, ew, eh); } if (centerY > rInnerMaxY) { return Ellipse2afp.intersectsEllipseEllipse(rx1, rInnerMaxY - ah, aw * 2, ah * 2, ex, ey, ew, eh); } return centerX > (rx1 - radius1) && centerX < (rx2 + radius1); } if (centerX > rInnerMaxX) { if (centerY < ry1) { return Ellipse2afp.intersectsEllipseEllipse(rInnerMaxX - aw, ry1, aw * 2, ah * 2, ex, ey, ew, eh); } if (centerY > rInnerMaxY) { return Ellipse2afp.intersectsEllipseEllipse(rInnerMaxX - aw, rInnerMaxY - ah, aw * 2, ah * 2, ex, ey, ew, eh); } return centerX > (rx1 - radius1) && centerX < (rx2 + radius1); } return centerY > (ry1 - radius2) && centerY < (ry2 + radius2); } @Pure @Override default boolean equalsToShape(IT shape) { if (shape == null) { return false; } if (shape == this) { return true; } return getMinX() == shape.getMinX() && getMinY() == shape.getMinY() && getMaxX() == shape.getMaxX() && getMaxY() == shape.getMaxY() && getArcWidth() == shape.getArcWidth() && getArcHeight() == shape.getArcHeight(); } /** * Gets the width of the arc that rounds off the corners. * @return the width of the arc that rounds off the corners * of this <code>RoundRectangle2afp</code>. */ @Pure double getArcWidth(); /** * Gets the height of the arc that rounds off the corners. * @return the height of the arc that rounds off the corners * of this <code>RoundRectangle2afp</code>. */ @Pure double getArcHeight(); /** * Set the width of the arc that rounds off the corners. * @param arcWidth is the width of the arc that rounds off the corners * of this <code>RoundRectangle2afp</code>. */ void setArcWidth(double arcWidth); /** * Set the height of the arc that rounds off the corners. * @param arcHeight is the height of the arc that rounds off the corners * of this <code>RoundRectangle2afp</code>. */ void setArcHeight(double arcHeight); /** Change the frame of the rectangle. * * @param x new x coordinate of the minimum corner. * @param y new y coordinate of the minimum corner. * @param width new width of the rectangle. * @param height new height of the rectangle. * @param arcWidth is the width of the arc that rounds off the corners * of this <code>RoundRectangle2afp</code>. * @param arcHeight is the height of the arc that rounds off the corners * of this <code>RoundRectangle2afp</code>. */ @SuppressWarnings("checkstyle:magicnumber") default void set(double x, double y, double width, double height, double arcWidth, double arcHeight) { assert width >= 0. : AssertMessages.positiveOrZeroParameter(2); assert height >= 0. : AssertMessages.positiveOrZeroParameter(3); assert arcWidth >= 0. : AssertMessages.positiveOrZeroParameter(4); assert arcHeight >= 0. : AssertMessages.positiveOrZeroParameter(5); setFromCorners(x, y, x + width, y + height, arcWidth, arcHeight); } @Override default void set(IT shape) { assert shape != null : AssertMessages.notNullParameter(); setFromCorners(shape.getMinX(), shape.getMinY(), shape.getMaxX(), shape.getMaxY(), shape.getArcWidth(), shape.getArcHeight()); } /** Change the frame of the rectangle. * * @param x1 is the coordinate of the first corner. * @param y1 is the coordinate of the first corner. * @param x2 is the coordinate of the second corner. * @param y2 is the coordinate of the second corner. * @param arcWidth is the width of the arc that rounds off the corners * of this <code>RoundRectangle2afp</code>. * @param arcHeight is the height of the arc that rounds off the corners * of this <code>RoundRectangle2afp</code>. */ void setFromCorners(double x1, double y1, double x2, double y2, double arcWidth, double arcHeight); @Override default boolean contains(double x, double y) { return containsRoundRectanglePoint( getMinX(), getMinY(), getWidth(), getHeight(), getArcWidth(), getArcHeight(), x, y); } @Override default boolean contains(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); return containsRoundRectangleRectangle( getMinX(), getMinY(), getWidth(), getHeight(), getArcWidth(), getArcHeight(), rectangle.getMinX(), rectangle.getMinY(), rectangle.getWidth(), rectangle.getHeight()); } @Pure @Override default double getDistanceSquared(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final Point2D<?, ?> n = getClosestPointTo(pt); return n.getDistanceSquared(pt); } @Pure @Override default double getDistanceL1(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final Point2D<?, ?> n = getClosestPointTo(pt); return n.getDistanceL1(pt); } @Pure @Override default double getDistanceLinf(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final Point2D<?, ?> n = getClosestPointTo(pt); return n.getDistanceLinf(pt); } @Override default boolean intersects(Circle2afp<?, ?, ?, ?, ?, ?> circle) { assert circle != null : AssertMessages.notNullParameter(); return intersectsRoundRectangleCircle( getMinX(), getMinY(), getMaxX(), getMaxY(), getArcWidth(), getArcHeight(), circle.getX(), circle.getY(), circle.getRadius()); } @Override default boolean intersects(Ellipse2afp<?, ?, ?, ?, ?, ?> ellipse) { assert ellipse != null : AssertMessages.notNullParameter(); return intersectsRoundRectangleEllipse( getMinX(), getMinY(), getMaxX(), getMaxY(), getArcWidth(), getArcHeight(), ellipse.getMinX(), ellipse.getMinY(), ellipse.getWidth(), ellipse.getHeight()); } @Override default boolean intersects(OrientedRectangle2afp<?, ?, ?, ?, ?, ?> orientedRectangle) { assert orientedRectangle != null : AssertMessages.notNullParameter(); return OrientedRectangle2afp.intersectsOrientedRectangleRoundRectangle( orientedRectangle.getCenterX(), orientedRectangle.getCenterY(), orientedRectangle.getFirstAxisX(), orientedRectangle.getFirstAxisY(), orientedRectangle.getFirstAxisExtent(), orientedRectangle.getSecondAxisExtent(), getMinX(), getMinY(), getWidth(), getHeight(), getArcWidth(), getArcHeight()); } @Override default boolean intersects(Parallelogram2afp<?, ?, ?, ?, ?, ?> oarallelogram) { assert oarallelogram != null : AssertMessages.notNullParameter(); return Parallelogram2afp.intersectsParallelogramRoundRectangle( oarallelogram.getCenterX(), oarallelogram.getCenterY(), oarallelogram.getFirstAxisX(), oarallelogram.getFirstAxisY(), oarallelogram.getFirstAxisExtent(), oarallelogram.getSecondAxisX(), oarallelogram.getSecondAxisY(), oarallelogram.getSecondAxisExtent(), getMinX(), getMinY(), getWidth(), getHeight(), getArcWidth(), getArcHeight()); } @Override default boolean intersects(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); return intersectsRoundRectangleRectangle( getMinX(), getMinY(), getMaxX(), getMaxY(), getArcWidth(), getArcHeight(), rectangle.getMinX(), rectangle.getMinY(), rectangle.getMaxX(), rectangle.getMaxY()); } @Override default boolean intersects(RoundRectangle2afp<?, ?, ?, ?, ?, ?> roundRectangle) { assert roundRectangle != null : AssertMessages.notNullParameter(); return intersectsRoundRectangleRoundRectangle( getMinX(), getMinY(), getMaxX(), getMaxY(), getArcWidth(), getArcHeight(), roundRectangle.getMinX(), roundRectangle.getMinY(), roundRectangle.getMaxX(), roundRectangle.getMaxY(), roundRectangle.getArcWidth(), roundRectangle.getArcHeight()); } @Override default boolean intersects(Segment2afp<?, ?, ?, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); return RoundRectangle2afp.intersectsRoundRectangleSegment( getMinX(), getMinY(), getMaxX(), getMaxY(), getArcWidth(), getArcHeight(), segment.getX1(), segment.getY1(), segment.getX2(), segment.getY2()); } @Override default boolean intersects(Triangle2afp<?, ?, ?, ?, ?, ?> triangle) { assert triangle != null : AssertMessages.notNullParameter(); return intersects(triangle.getPathIterator()); } @Override default boolean intersects(PathIterator2afp<?> iterator) { assert iterator != null : AssertMessages.notNullParameter(); final int mask = iterator.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2; final int crossings = Path2afp.calculatesCrossingsPathIteratorRoundRectangleShadow( 0, iterator, getMinX(), getMinY(), getMaxX(), getMaxY(), getArcWidth(), getArcHeight(), CrossingComputationType.SIMPLE_INTERSECTION_WHEN_NOT_POLYGON); return crossings == MathConstants.SHAPE_INTERSECTS || (crossings & mask) != 0; } @Pure @Override default boolean intersects(MultiShape2afp<?, ?, ?, ?, ?, ?, ?> multishape) { assert multishape != null : AssertMessages.notNullParameter(); return multishape.intersects(this); } /** {@inheritDoc} * @see #getFlatteningPathIterator() */ @Pure @Override default PathIterator2afp<IE> getPathIterator(Transform2D transform) { if (transform == null || transform.isIdentity()) { return new RoundRectanglePathIterator<>(this); } return new TransformedRoundRectanglePathIterator<>(this, transform); } /** Replies a path iterator on this round rectangle that is replacing the * corner arcs by line approximations. * * @return the iterator on the approximation. * @see #getPathIterator() * @see MathConstants#SPLINE_APPROXIMATION_RATIO */ @Pure @SuppressWarnings("checkstyle:magicnumber") default PathIterator2afp<IE> getFlatteningPathIterator() { // TODO: Remove this part of the code when SPLINE_APPROXIMATION_RATIO is decreased. return new Path2afp.FlatteningPathIterator<>(getPathIterator(null), MathConstants.SPLINE_APPROXIMATION_RATIO / 10., Path2afp.DEFAULT_FLATTENING_LIMIT); } @Pure @Override @SuppressWarnings("checkstyle:npathcomplexity") default P getClosestPointTo(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final double px = pt.getX(); final double py = pt.getY(); final double rx1 = getMinX(); final double ry1 = getMinY(); final double rx2 = getMaxX(); final double ry2 = getMaxY(); final double aw = getArcWidth(); final double ah = getArcHeight(); final GeomFactory2afp<?, P, V, ?> factory = getGeomFactory(); if (px < rx1 + aw) { if (py < ry1 + ah) { final P point = factory.newPoint(); Ellipse2afp.findsClosestPointSolidEllipsePoint( px, py, rx1, ry1, aw * 2, ah * 2, point); return point; } if (py > ry2 - ah) { final double eh = ah * 2; final P point = factory.newPoint(); Ellipse2afp.findsClosestPointSolidEllipsePoint( px, py, rx1, ry2 - eh, aw * 2, eh, point); return point; } } else if (px > rx2 - aw) { if (py < ry1 + ah) { final double ew = aw * 2; final P point = factory.newPoint(); Ellipse2afp.findsClosestPointSolidEllipsePoint( px, py, rx2 - ew, ry1, ew, ah * 2, point); return point; } if (py > ry2 - ah) { final double ew = aw * 2; final double eh = ah * 2; final P point = factory.newPoint(); Ellipse2afp.findsClosestPointSolidEllipsePoint( px, py, rx2 - ew, ry2 - eh, ew, eh, point); return point; } } int same = 0; final double x; final double y; if (px < rx1) { x = rx1; } else if (px > rx2) { x = rx2; } else { x = px; ++same; } if (py < ry1) { y = ry1; } else if (py > ry2) { y = ry2; } else { y = py; ++same; } if (same == 2) { return factory.convertToPoint(pt); } return factory.newPoint(x, y); } @Override default P getClosestPointTo(Circle2afp<?, ?, ?, ?, ?, ?> circle) { assert circle != null : AssertMessages.notNullParameter(); return getClosestPointTo(circle.getCenter()); } @Override @Unefficient default P getClosestPointTo(Ellipse2afp<?, ?, ?, ?, ?, ?> ellipse) { assert ellipse != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getFlatteningPathIterator(), ellipse.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getFlatteningPathIterator(), rectangle.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(Segment2afp<?, ?, ?, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); final Point2D<?, ?> pointOnSegment = segment.getClosestPointTo(this); return getClosestPointTo(pointOnSegment); } @Override @Unefficient default P getClosestPointTo(Triangle2afp<?, ?, ?, ?, ?, ?> triangle) { assert triangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getFlatteningPathIterator(), triangle.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(OrientedRectangle2afp<?, ?, ?, ?, ?, ?> orientedRectangle) { assert orientedRectangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getFlatteningPathIterator(), orientedRectangle.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(Parallelogram2afp<?, ?, ?, ?, ?, ?> parallelogram) { assert parallelogram != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getFlatteningPathIterator(), parallelogram.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(RoundRectangle2afp<?, ?, ?, ?, ?, ?> roundRectangle) { assert roundRectangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getFlatteningPathIterator(), roundRectangle.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(Path2afp<?, ?, ?, ?, ?, ?> path) { assert path != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getFlatteningPathIterator(), path.getPathIterator(), point); return point; } @Pure @Override default P getFarthestPointTo(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final double px = pt.getX(); final double py = pt.getY(); final double rx1 = getMinX(); final double ry1 = getMinY(); final double rx2 = getMaxX(); final double ry2 = getMaxY(); final double centerX = getCenterX(); final double centerY = getCenterY(); final double aw = getArcWidth(); final double ah = getArcHeight(); final P point = getGeomFactory().newPoint(); if (px <= centerX) { if (py <= centerY) { final double ew = aw * 2; final double eh = ah * 2; Ellipse2afp.findsFarthestPointShallowEllipsePoint( px, py, rx2 - ew, ry2 - eh, ew, eh, point); } else { final double ew = aw * 2; Ellipse2afp.findsFarthestPointShallowEllipsePoint( px, py, rx2 - ew, ry1, ew, ah * 2, point); } } else if (px <= centerY) { final double eh = ah * 2; Ellipse2afp.findsFarthestPointShallowEllipsePoint( px, py, rx1, ry2 - eh, aw * 2, eh, point); } else { Ellipse2afp.findsFarthestPointShallowEllipsePoint( px, py, rx1, ry1, aw * 2, ah * 2, point); } return point; } /** Abstract iterator on the path elements of the round rectangle. * * @param <T> the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ abstract class AbstractRoundRectanglePathIterator<T extends PathElement2afp> implements PathIterator2afp<T> { /** Number of elements in the path (except move). */ protected static final int ELEMENT_COUNT = 9; /** The iterator round rectangle. */ protected final RoundRectangle2afp<?, ?, T, ?, ?, ?> rectangle; /** * @param rectangle the iterated rectangle. */ public AbstractRoundRectanglePathIterator(RoundRectangle2afp<?, ?, T, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); this.rectangle = rectangle; } @Override public GeomFactory2afp<T, ?, ?, ?> getGeomFactory() { return this.rectangle.getGeomFactory(); } @Override public void remove() { throw new UnsupportedOperationException(); } @Pure @Override public PathWindingRule getWindingRule() { return PathWindingRule.NON_ZERO; } @Pure @Override public boolean isPolyline() { return false; } @Pure @Override public boolean isCurved() { return true; } @Pure @Override public boolean isPolygon() { return true; } @Pure @Override public boolean isMultiParts() { return false; } } /** Iterator on the path elements of the rectangle. * * @param <T> the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ class RoundRectanglePathIterator<T extends PathElement2afp> extends AbstractRoundRectanglePathIterator<T> { private double x; private double y; private double width; private double height; private double arcWidth; private double arcHeight; private int index; private double lastX; private double lastY; private double moveX; private double moveY; /** * @param rectangle the round rectangle to iterate on. */ public RoundRectanglePathIterator(RoundRectangle2afp<?, ?, T, ?, ?, ?> rectangle) { super(rectangle); if (rectangle.isEmpty()) { this.index = ELEMENT_COUNT; } else { this.x = rectangle.getMinX(); this.y = rectangle.getMinY(); this.width = rectangle.getWidth(); this.height = rectangle.getHeight(); this.arcWidth = rectangle.getArcWidth(); this.arcHeight = rectangle.getArcHeight(); this.index = -1; } } @Override public PathIterator2afp<T> restartIterations() { return new RoundRectanglePathIterator<>(this.rectangle); } @Pure @Override public boolean hasNext() { return this.index < ELEMENT_COUNT; } @Override @SuppressWarnings({"checkstyle:returncount", "checkstyle:magicnumber"}) public T next() { if (this.index >= ELEMENT_COUNT) { throw new NoSuchElementException(); } final int idx = this.index; ++this.index; if (idx < 0) { this.moveX = this.x + this.arcWidth; this.moveY = this.y; this.lastX = this.moveX; this.lastY = this.moveY; return getGeomFactory().newMovePathElement( this.lastX, this.lastY); } final double x = this.lastX; final double y = this.lastY; final double curveX; final double curveY; switch (idx) { case 0: this.lastX = this.x + this.width - this.arcWidth; return getGeomFactory().newLinePathElement( x, y, this.lastX, this.lastY); case 1: this.lastX += this.arcWidth; this.lastY += this.arcHeight; curveX = x + AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcWidth; curveY = this.lastY - AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcHeight; return getGeomFactory().newCurvePathElement( x, y, curveX, y, this.lastX, curveY, this.lastX, this.lastY); case 2: this.lastY = this.y + this.height - this.arcHeight; return getGeomFactory().newLinePathElement( x, y, this.lastX, this.lastY); case 3: this.lastX -= this.arcWidth; this.lastY += this.arcHeight; curveX = this.lastX + AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcWidth; curveY = y + AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcHeight; return getGeomFactory().newCurvePathElement( x, y, x, curveY, curveX, this.lastY, this.lastX, this.lastY); case 4: this.lastX = this.x + this.arcWidth; return getGeomFactory().newLinePathElement( x, y, this.lastX, this.lastY); case 5: this.lastX -= this.arcWidth; this.lastY -= this.arcHeight; curveX = x - AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcWidth; curveY = this.lastY + AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcHeight; return getGeomFactory().newCurvePathElement( x, y, curveX, y, this.lastX, curveY, this.lastX, this.lastY); case 6: this.lastY = this.y + this.arcHeight; return getGeomFactory().newLinePathElement( x, y, this.lastX, this.lastY); case 7: this.lastX += this.arcWidth; this.lastY -= this.arcHeight; curveX = this.lastX - AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcWidth; curveY = y - AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcHeight; return getGeomFactory().newCurvePathElement( x, y, x, curveY, curveX, this.lastY, this.lastX, this.lastY); default: return getGeomFactory().newClosePathElement( x, y, this.moveX, this.moveY); } } } /** Iterator on the path elements of the rectangle. * * @param <T> the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ class TransformedRoundRectanglePathIterator<T extends PathElement2afp> extends AbstractRoundRectanglePathIterator<T> { private final Transform2D transform; private double x; private double y; private double width; private double height; private double arcWidth; private double arcHeight; private int index; private Point2D<?, ?> last; private Point2D<?, ?> move; private Point2D<?, ?> controlPoint1; private Point2D<?, ?> controlPoint2; /** * @param rectangle the round rectangle to iterate on. * @param transform the transformation. */ public TransformedRoundRectanglePathIterator(RoundRectangle2afp<?, ?, T, ?, ?, ?> rectangle, Transform2D transform) { super(rectangle); assert transform != null : AssertMessages.notNullParameter(1); this.transform = transform; if (rectangle.isEmpty()) { this.index = ELEMENT_COUNT; } else { this.last = new InnerComputationPoint2afp(); this.move = new InnerComputationPoint2afp(); this.controlPoint1 = new InnerComputationPoint2afp(); this.controlPoint2 = new InnerComputationPoint2afp(); this.x = rectangle.getMinX(); this.y = rectangle.getMinY(); this.width = rectangle.getWidth(); this.height = rectangle.getHeight(); this.arcWidth = rectangle.getArcWidth(); this.arcHeight = rectangle.getArcHeight(); this.index = -1; } } @Override public PathIterator2afp<T> restartIterations() { return new TransformedRoundRectanglePathIterator<>(this.rectangle, this.transform); } @Pure @Override public boolean hasNext() { return this.index < ELEMENT_COUNT; } @Override @SuppressWarnings({"checkstyle:returncount", "checkstyle:magicnumber"}) public T next() { if (this.index >= ELEMENT_COUNT) { throw new NoSuchElementException(); } final int idx = this.index; ++this.index; if (idx < 0) { this.move.set(this.x + this.arcWidth, this.y); this.transform.transform(this.move); this.last.set(this.move); return getGeomFactory().newMovePathElement( this.last.getX(), this.last.getY()); } final double x = this.last.getX(); final double y = this.last.getY(); switch (idx) { case 0: this.last.set( this.x + this.width - this.arcWidth, this.y); this.transform.transform(this.last); return getGeomFactory().newLinePathElement( x, y, this.last.getX(), this.last.getY()); case 1: this.last.set( this.x + this.width, this.y + this.arcHeight); this.controlPoint1.set( this.x + this.width - this.arcWidth + AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcWidth, this.y); this.controlPoint2.set( this.x + this.width, this.y + this.arcHeight - AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcHeight); this.transform.transform(this.last); this.transform.transform(this.controlPoint1); this.transform.transform(this.controlPoint2); return getGeomFactory().newCurvePathElement( x, y, this.controlPoint1.getX(), this.controlPoint1.getY(), this.controlPoint2.getX(), this.controlPoint2.getY(), this.last.getX(), this.last.getY()); case 2: this.last.set( this.x + this.width, this.y + this.height - this.arcHeight); this.transform.transform(this.last); return getGeomFactory().newLinePathElement( x, y, this.last.getX(), this.last.getY()); case 3: this.last.set( this.x + this.width - this.arcWidth, this.y + this.height); this.controlPoint1.set( this.x + this.width, this.y + this.height - this.arcHeight + AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcHeight); this.controlPoint2.set( this.x + this.width - this.arcWidth + AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcWidth, this.y + this.height); this.transform.transform(this.last); this.transform.transform(this.controlPoint1); this.transform.transform(this.controlPoint2); return getGeomFactory().newCurvePathElement( x, y, this.controlPoint1.getX(), this.controlPoint1.getY(), this.controlPoint2.getX(), this.controlPoint2.getY(), this.last.getX(), this.last.getY()); case 4: this.last.set( this.x + this.arcWidth, this.y + this.height); this.transform.transform(this.last); return getGeomFactory().newLinePathElement( x, y, this.last.getX(), this.last.getY()); case 5: this.last.set( this.x, this.y + this.height - this.arcHeight); this.controlPoint1.set( this.x + this.arcWidth - AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcWidth, this.y + this.height); this.controlPoint2.set( this.x, this.y + this.height - this.arcHeight + AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcHeight); this.transform.transform(this.last); this.transform.transform(this.controlPoint1); this.transform.transform(this.controlPoint2); return getGeomFactory().newCurvePathElement( x, y, this.controlPoint1.getX(), this.controlPoint1.getY(), this.controlPoint2.getX(), this.controlPoint2.getY(), this.last.getX(), this.last.getY()); case 6: this.last.set( this.x, this.y + this.arcHeight); this.transform.transform(this.last); return getGeomFactory().newLinePathElement( x, y, this.last.getX(), this.last.getY()); case 7: this.controlPoint1.set( this.x, this.y + this.arcHeight - AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcHeight); this.controlPoint2.set( this.x + this.arcWidth - AbstractCirclePathIterator.CTRL_POINT_DISTANCE * this.arcWidth, this.y); this.transform.transform(this.controlPoint1); this.transform.transform(this.controlPoint2); return getGeomFactory().newCurvePathElement( x, y, this.controlPoint1.getX(), this.controlPoint1.getY(), this.controlPoint2.getX(), this.controlPoint2.getY(), this.move.getX(), this.move.getY()); default: return getGeomFactory().newClosePathElement( x, y, this.move.getX(), this.move.getY()); } } } }