/* * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.awt.geom; import java.awt.geom.Rectangle2D;import java.awt.geom.QuadCurve2D;import java.awt.geom.CubicCurve2D;import java.awt.geom.PathIterator;import java.awt.geom.IllegalPathStateException;import java.util.Vector; public abstract class Curve { public static final int INCREASING = 1; public static final int DECREASING = -1; protected int direction; public static void insertMove(Vector curves, double x, double y) { curves.add(new Order0(x, y)); } public static void insertLine(Vector curves, double x0, double y0, double x1, double y1) { if (y0 < y1) { curves.add(new Order1(x0, y0, x1, y1, INCREASING)); } else if (y0 > y1) { curves.add(new Order1(x1, y1, x0, y0, DECREASING)); } else { // Do not add horizontal lines } } public static void insertQuad(Vector curves, double x0, double y0, double coords[]) { double y1 = coords[3]; if (y0 > y1) { Order2.insert(curves, coords, coords[2], y1, coords[0], coords[1], x0, y0, DECREASING); } else if (y0 == y1 && y0 == coords[1]) { // Do not add horizontal lines return; } else { Order2.insert(curves, coords, x0, y0, coords[0], coords[1], coords[2], y1, INCREASING); } } public static void insertCubic(Vector curves, double x0, double y0, double coords[]) { double y1 = coords[5]; if (y0 > y1) { Order3.insert(curves, coords, coords[4], y1, coords[2], coords[3], coords[0], coords[1], x0, y0, DECREASING); } else if (y0 == y1 && y0 == coords[1] && y0 == coords[3]) { // Do not add horizontal lines return; } else { Order3.insert(curves, coords, x0, y0, coords[0], coords[1], coords[2], coords[3], coords[4], y1, INCREASING); } } /** * Calculates the number of times the given path * crosses the ray extending to the right from (px,py). * If the point lies on a part of the path, * then no crossings are counted for that intersection. * +1 is added for each crossing where the Y coordinate is increasing * -1 is added for each crossing where the Y coordinate is decreasing * The return value is the sum of all crossings for every segment in * the path. * The path must start with a SEG_MOVETO, otherwise an exception is * thrown. * The caller must check p[xy] for NaN values. * The caller may also reject infinite p[xy] values as well. */ public static int pointCrossingsForPath(PathIterator pi, double px, double py) { if (pi.isDone()) { return 0; } double coords[] = new double[6]; if (pi.currentSegment(coords) != PathIterator.SEG_MOVETO) { throw new IllegalPathStateException("missing initial moveto "+ "in path definition"); } pi.next(); double movx = coords[0]; double movy = coords[1]; double curx = movx; double cury = movy; double endx, endy; int crossings = 0; while (!pi.isDone()) { switch (pi.currentSegment(coords)) { case PathIterator.SEG_MOVETO: if (cury != movy) { crossings += pointCrossingsForLine(px, py, curx, cury, movx, movy); } movx = curx = coords[0]; movy = cury = coords[1]; break; case PathIterator.SEG_LINETO: endx = coords[0]; endy = coords[1]; crossings += pointCrossingsForLine(px, py, curx, cury, endx, endy); curx = endx; cury = endy; break; case PathIterator.SEG_QUADTO: endx = coords[2]; endy = coords[3]; crossings += pointCrossingsForQuad(px, py, curx, cury, coords[0], coords[1], endx, endy, 0); curx = endx; cury = endy; break; case PathIterator.SEG_CUBICTO: endx = coords[4]; endy = coords[5]; crossings += pointCrossingsForCubic(px, py, curx, cury, coords[0], coords[1], coords[2], coords[3], endx, endy, 0); curx = endx; cury = endy; break; case PathIterator.SEG_CLOSE: if (cury != movy) { crossings += pointCrossingsForLine(px, py, curx, cury, movx, movy); } curx = movx; cury = movy; break; } pi.next(); } if (cury != movy) { crossings += pointCrossingsForLine(px, py, curx, cury, movx, movy); } return crossings; } /** * Calculates the number of times the line from (x0,y0) to (x1,y1) * crosses the ray extending to the right from (px,py). * If the point lies on the line, then no crossings are recorded. * +1 is returned for a crossing where the Y coordinate is increasing * -1 is returned for a crossing where the Y coordinate is decreasing */ public static int pointCrossingsForLine(double px, double py, double x0, double y0, double x1, double y1) { if (py < y0 && py < y1) return 0; if (py >= y0 && py >= y1) return 0; // assert(y0 != y1); if (px >= x0 && px >= x1) return 0; if (px < x0 && px < x1) return (y0 < y1) ? 1 : -1; double xintercept = x0 + (py - y0) * (x1 - x0) / (y1 - y0); if (px >= xintercept) return 0; return (y0 < y1) ? 1 : -1; } /** * Calculates the number of times the quad from (x0,y0) to (x1,y1) * crosses the ray extending to the right from (px,py). * If the point lies on a part of the curve, * then no crossings are counted for that intersection. * the level parameter should be 0 at the top-level call and will count * up for each recursion level to prevent infinite recursion * +1 is added for each crossing where the Y coordinate is increasing * -1 is added for each crossing where the Y coordinate is decreasing */ public static int pointCrossingsForQuad(double px, double py, double x0, double y0, double xc, double yc, double x1, double y1, int level) { if (py < y0 && py < yc && py < y1) return 0; if (py >= y0 && py >= yc && py >= y1) return 0; // Note y0 could equal y1... if (px >= x0 && px >= xc && px >= x1) return 0; if (px < x0 && px < xc && px < x1) { if (py >= y0) { if (py < y1) return 1; } else { // py < y0 if (py >= y1) return -1; } // py outside of y01 range, and/or y0==y1 return 0; } // double precision only has 52 bits of mantissa if (level > 52) return pointCrossingsForLine(px, py, x0, y0, x1, y1); double x0c = (x0 + xc) / 2; double y0c = (y0 + yc) / 2; double xc1 = (xc + x1) / 2; double yc1 = (yc + y1) / 2; xc = (x0c + xc1) / 2; yc = (y0c + yc1) / 2; if (Double.isNaN(xc) || Double.isNaN(yc)) { // [xy]c are NaN if any of [xy]0c or [xy]c1 are NaN // [xy]0c or [xy]c1 are NaN if any of [xy][0c1] are NaN // These values are also NaN if opposing infinities are added return 0; } return (pointCrossingsForQuad(px, py, x0, y0, x0c, y0c, xc, yc, level+1) + pointCrossingsForQuad(px, py, xc, yc, xc1, yc1, x1, y1, level+1)); } /** * Calculates the number of times the cubic from (x0,y0) to (x1,y1) * crosses the ray extending to the right from (px,py). * If the point lies on a part of the curve, * then no crossings are counted for that intersection. * the level parameter should be 0 at the top-level call and will count * up for each recursion level to prevent infinite recursion * +1 is added for each crossing where the Y coordinate is increasing * -1 is added for each crossing where the Y coordinate is decreasing */ public static int pointCrossingsForCubic(double px, double py, double x0, double y0, double xc0, double yc0, double xc1, double yc1, double x1, double y1, int level) { if (py < y0 && py < yc0 && py < yc1 && py < y1) return 0; if (py >= y0 && py >= yc0 && py >= yc1 && py >= y1) return 0; // Note y0 could equal yc0... if (px >= x0 && px >= xc0 && px >= xc1 && px >= x1) return 0; if (px < x0 && px < xc0 && px < xc1 && px < x1) { if (py >= y0) { if (py < y1) return 1; } else { // py < y0 if (py >= y1) return -1; } // py outside of y01 range, and/or y0==yc0 return 0; } // double precision only has 52 bits of mantissa if (level > 52) return pointCrossingsForLine(px, py, x0, y0, x1, y1); double xmid = (xc0 + xc1) / 2; double ymid = (yc0 + yc1) / 2; xc0 = (x0 + xc0) / 2; yc0 = (y0 + yc0) / 2; xc1 = (xc1 + x1) / 2; yc1 = (yc1 + y1) / 2; double xc0m = (xc0 + xmid) / 2; double yc0m = (yc0 + ymid) / 2; double xmc1 = (xmid + xc1) / 2; double ymc1 = (ymid + yc1) / 2; xmid = (xc0m + xmc1) / 2; ymid = (yc0m + ymc1) / 2; if (Double.isNaN(xmid) || Double.isNaN(ymid)) { // [xy]mid are NaN if any of [xy]c0m or [xy]mc1 are NaN // [xy]c0m or [xy]mc1 are NaN if any of [xy][c][01] are NaN // These values are also NaN if opposing infinities are added return 0; } return (pointCrossingsForCubic(px, py, x0, y0, xc0, yc0, xc0m, yc0m, xmid, ymid, level+1) + pointCrossingsForCubic(px, py, xmid, ymid, xmc1, ymc1, xc1, yc1, x1, y1, level+1)); } /** * The rectangle intersection test counts the number of times * that the path crosses through the shadow that the rectangle * projects to the right towards (x => +INFINITY). * * During processing of the path it actually counts every time * the path crosses either or both of the top and bottom edges * of that shadow. If the path enters from the top, the count * is incremented. If it then exits back through the top, the * same way it came in, the count is decremented and there is * no impact on the winding count. If, instead, the path exits * out the bottom, then the count is incremented again and a * full pass through the shadow is indicated by the winding count * having been incremented by 2. * * Thus, the winding count that it accumulates is actually double * the real winding count. Since the path is continuous, the * final answer should be a multiple of 2, otherwise there is a * logic error somewhere. * * If the path ever has a direct hit on the rectangle, then a * special value is returned. This special value terminates * all ongoing accumulation on up through the call chain and * ends up getting returned to the calling function which can * then produce an answer directly. For intersection tests, * the answer is always "true" if the path intersects the * rectangle. For containment tests, the answer is always * "false" if the path intersects the rectangle. Thus, no * further processing is ever needed if an intersection occurs. */ public static final int RECT_INTERSECTS = 0x80000000; /** * Accumulate the number of times the path crosses the shadow * extending to the right of the rectangle. See the comment * for the RECT_INTERSECTS constant for more complete details. * The return value is the sum of all crossings for both the * top and bottom of the shadow for every segment in the path, * or the special value RECT_INTERSECTS if the path ever enters * the interior of the rectangle. * The path must start with a SEG_MOVETO, otherwise an exception is * thrown. * The caller must check r[xy]{min,max} for NaN values. */ public static int rectCrossingsForPath(PathIterator pi, double rxmin, double rymin, double rxmax, double rymax) { if (rxmax <= rxmin || rymax <= rymin) { return 0; } if (pi.isDone()) { return 0; } double coords[] = new double[6]; if (pi.currentSegment(coords) != PathIterator.SEG_MOVETO) { throw new IllegalPathStateException("missing initial moveto "+ "in path definition"); } pi.next(); double curx, cury, movx, movy, endx, endy; curx = movx = coords[0]; cury = movy = coords[1]; int crossings = 0; while (crossings != RECT_INTERSECTS && !pi.isDone()) { switch (pi.currentSegment(coords)) { case PathIterator.SEG_MOVETO: if (curx != movx || cury != movy) { crossings = rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy); } // Count should always be a multiple of 2 here. // assert((crossings & 1) != 0); movx = curx = coords[0]; movy = cury = coords[1]; break; case PathIterator.SEG_LINETO: endx = coords[0]; endy = coords[1]; crossings = rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, endx, endy); curx = endx; cury = endy; break; case PathIterator.SEG_QUADTO: endx = coords[2]; endy = coords[3]; crossings = rectCrossingsForQuad(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[0], coords[1], endx, endy, 0); curx = endx; cury = endy; break; case PathIterator.SEG_CUBICTO: endx = coords[4]; endy = coords[5]; crossings = rectCrossingsForCubic(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[0], coords[1], coords[2], coords[3], endx, endy, 0); curx = endx; cury = endy; break; case PathIterator.SEG_CLOSE: if (curx != movx || cury != movy) { crossings = rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy); } curx = movx; cury = movy; // Count should always be a multiple of 2 here. // assert((crossings & 1) != 0); break; } pi.next(); } if (crossings != RECT_INTERSECTS && (curx != movx || cury != movy)) { crossings = rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy); } // Count should always be a multiple of 2 here. // assert((crossings & 1) != 0); return crossings; } /** * Accumulate the number of times the line crosses the shadow * extending to the right of the rectangle. See the comment * for the RECT_INTERSECTS constant for more complete details. */ public static int rectCrossingsForLine(int crossings, double rxmin, double rymin, double rxmax, double rymax, double x0, double y0, double x1, double y1) { if (y0 >= rymax && y1 >= rymax) return crossings; if (y0 <= rymin && y1 <= rymin) return crossings; if (x0 <= rxmin && x1 <= rxmin) return crossings; if (x0 >= rxmax && x1 >= rxmax) { // Line is entirely to the right of the rect // and the vertical ranges of the two overlap by a non-empty amount // Thus, this line segment is partially in the "right-shadow" // Path may have done a complete crossing // Or path may have entered or exited the right-shadow if (y0 < y1) { // y-increasing line segment... // We know that y0 < rymax and y1 > rymin if (y0 <= rymin) crossings++; if (y1 >= rymax) crossings++;Lines 1–500 of 1206. Load more
/* * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.awt.geom; import java.awt.geom.Rectangle2D; import java.awt.geom.QuadCurve2D; import java.awt.geom.CubicCurve2D; import java.awt.geom.PathIterator; import java.awt.geom.IllegalPathStateException; import java.util.Vector; public abstract class Curve { public static final int INCREASING = 1; public static final int DECREASING = -1; protected int direction; public static void insertMove(Vector curves, double x, double y) { curves.add(new Order0(x, y)); } public static void insertLine(Vector curves, double x0, double y0, double x1, double y1) { if (y0 < y1) { curves.add(new Order1(x0, y0, x1, y1, INCREASING)); } else if (y0 > y1) { curves.add(new Order1(x1, y1, x0, y0, DECREASING)); } else { // Do not add horizontal lines } } public static void insertQuad(Vector curves, double x0, double y0, double coords[]) { double y1 = coords[3]; if (y0 > y1) { Order2.insert(curves, coords, coords[2], y1, coords[0], coords[1], x0, y0, DECREASING); } else if (y0 == y1 && y0 == coords[1]) { // Do not add horizontal lines return; } else { Order2.insert(curves, coords, x0, y0, coords[0], coords[1], coords[2], y1, INCREASING); } } public static void insertCubic(Vector curves, double x0, double y0, double coords[]) { double y1 = coords[5]; if (y0 > y1) { Order3.insert(curves, coords, coords[4], y1, coords[2], coords[3], coords[0], coords[1], x0, y0, DECREASING); } else if (y0 == y1 && y0 == coords[1] && y0 == coords[3]) { // Do not add horizontal lines return; } else { Order3.insert(curves, coords, x0, y0, coords[0], coords[1], coords[2], coords[3], coords[4], y1, INCREASING); } } /** * Calculates the number of times the given path * crosses the ray extending to the right from (px,py). * If the point lies on a part of the path, * then no crossings are counted for that intersection. * +1 is added for each crossing where the Y coordinate is increasing * -1 is added for each crossing where the Y coordinate is decreasing * The return value is the sum of all crossings for every segment in * the path. * The path must start with a SEG_MOVETO, otherwise an exception is * thrown. * The caller must check p[xy] for NaN values. * The caller may also reject infinite p[xy] values as well. */ public static int pointCrossingsForPath(PathIterator pi, double px, double py) { if (pi.isDone()) { return 0; } double coords[] = new double[6]; if (pi.currentSegment(coords) != PathIterator.SEG_MOVETO) { throw new IllegalPathStateException("missing initial moveto "+ "in path definition"); } pi.next(); double movx = coords[0]; double movy = coords[1]; double curx = movx; double cury = movy; double endx, endy; int crossings = 0; while (!pi.isDone()) { switch (pi.currentSegment(coords)) { case PathIterator.SEG_MOVETO: if (cury != movy) { crossings += pointCrossingsForLine(px, py, curx, cury, movx, movy); } movx = curx = coords[0]; movy = cury = coords[1]; break; case PathIterator.SEG_LINETO: endx = coords[0]; endy = coords[1]; crossings += pointCrossingsForLine(px, py, curx, cury, endx, endy); curx = endx; cury = endy; break; case PathIterator.SEG_QUADTO: endx = coords[2]; endy = coords[3]; crossings += pointCrossingsForQuad(px, py, curx, cury, coords[0], coords[1], endx, endy, 0); curx = endx; cury = endy; break; case PathIterator.SEG_CUBICTO: endx = coords[4]; endy = coords[5]; crossings += pointCrossingsForCubic(px, py, curx, cury, coords[0], coords[1], coords[2], coords[3], endx, endy, 0); curx = endx; cury = endy; break; case PathIterator.SEG_CLOSE: if (cury != movy) { crossings += pointCrossingsForLine(px, py, curx, cury, movx, movy); } curx = movx; cury = movy; break; } pi.next(); } if (cury != movy) { crossings += pointCrossingsForLine(px, py, curx, cury, movx, movy); } return crossings; } /** * Calculates the number of times the line from (x0,y0) to (x1,y1) * crosses the ray extending to the right from (px,py). * If the point lies on the line, then no crossings are recorded. * +1 is returned for a crossing where the Y coordinate is increasing * -1 is returned for a crossing where the Y coordinate is decreasing */ public static int pointCrossingsForLine(double px, double py, double x0, double y0, double x1, double y1) { if (py < y0 && py < y1) return 0; if (py >= y0 && py >= y1) return 0; // assert(y0 != y1); if (px >= x0 && px >= x1) return 0; if (px < x0 && px < x1) return (y0 < y1) ? 1 : -1; double xintercept = x0 + (py - y0) * (x1 - x0) / (y1 - y0); if (px >= xintercept) return 0; return (y0 < y1) ? 1 : -1; } /** * Calculates the number of times the quad from (x0,y0) to (x1,y1) * crosses the ray extending to the right from (px,py). * If the point lies on a part of the curve, * then no crossings are counted for that intersection. * the level parameter should be 0 at the top-level call and will count * up for each recursion level to prevent infinite recursion * +1 is added for each crossing where the Y coordinate is increasing * -1 is added for each crossing where the Y coordinate is decreasing */ public static int pointCrossingsForQuad(double px, double py, double x0, double y0, double xc, double yc, double x1, double y1, int level) { if (py < y0 && py < yc && py < y1) return 0; if (py >= y0 && py >= yc && py >= y1) return 0; // Note y0 could equal y1... if (px >= x0 && px >= xc && px >= x1) return 0; if (px < x0 && px < xc && px < x1) { if (py >= y0) { if (py < y1) return 1; } else { // py < y0 if (py >= y1) return -1; } // py outside of y01 range, and/or y0==y1 return 0; } // double precision only has 52 bits of mantissa if (level > 52) return pointCrossingsForLine(px, py, x0, y0, x1, y1); double x0c = (x0 + xc) / 2; double y0c = (y0 + yc) / 2; double xc1 = (xc + x1) / 2; double yc1 = (yc + y1) / 2; xc = (x0c + xc1) / 2; yc = (y0c + yc1) / 2; if (Double.isNaN(xc) || Double.isNaN(yc)) { // [xy]c are NaN if any of [xy]0c or [xy]c1 are NaN // [xy]0c or [xy]c1 are NaN if any of [xy][0c1] are NaN // These values are also NaN if opposing infinities are added return 0; } return (pointCrossingsForQuad(px, py, x0, y0, x0c, y0c, xc, yc, level+1) + pointCrossingsForQuad(px, py, xc, yc, xc1, yc1, x1, y1, level+1)); } /** * Calculates the number of times the cubic from (x0,y0) to (x1,y1) * crosses the ray extending to the right from (px,py). * If the point lies on a part of the curve, * then no crossings are counted for that intersection. * the level parameter should be 0 at the top-level call and will count * up for each recursion level to prevent infinite recursion * +1 is added for each crossing where the Y coordinate is increasing * -1 is added for each crossing where the Y coordinate is decreasing */ public static int pointCrossingsForCubic(double px, double py, double x0, double y0, double xc0, double yc0, double xc1, double yc1, double x1, double y1, int level) { if (py < y0 && py < yc0 && py < yc1 && py < y1) return 0; if (py >= y0 && py >= yc0 && py >= yc1 && py >= y1) return 0; // Note y0 could equal yc0... if (px >= x0 && px >= xc0 && px >= xc1 && px >= x1) return 0; if (px < x0 && px < xc0 && px < xc1 && px < x1) { if (py >= y0) { if (py < y1) return 1; } else { // py < y0 if (py >= y1) return -1; } // py outside of y01 range, and/or y0==yc0 return 0; } // double precision only has 52 bits of mantissa if (level > 52) return pointCrossingsForLine(px, py, x0, y0, x1, y1); double xmid = (xc0 + xc1) / 2; double ymid = (yc0 + yc1) / 2; xc0 = (x0 + xc0) / 2; yc0 = (y0 + yc0) / 2; xc1 = (xc1 + x1) / 2; yc1 = (yc1 + y1) / 2; double xc0m = (xc0 + xmid) / 2; double yc0m = (yc0 + ymid) / 2; double xmc1 = (xmid + xc1) / 2; double ymc1 = (ymid + yc1) / 2; xmid = (xc0m + xmc1) / 2; ymid = (yc0m + ymc1) / 2; if (Double.isNaN(xmid) || Double.isNaN(ymid)) { // [xy]mid are NaN if any of [xy]c0m or [xy]mc1 are NaN // [xy]c0m or [xy]mc1 are NaN if any of [xy][c][01] are NaN // These values are also NaN if opposing infinities are added return 0; } return (pointCrossingsForCubic(px, py, x0, y0, xc0, yc0, xc0m, yc0m, xmid, ymid, level+1) + pointCrossingsForCubic(px, py, xmid, ymid, xmc1, ymc1, xc1, yc1, x1, y1, level+1)); } /** * The rectangle intersection test counts the number of times * that the path crosses through the shadow that the rectangle * projects to the right towards (x => +INFINITY). * * During processing of the path it actually counts every time * the path crosses either or both of the top and bottom edges * of that shadow. If the path enters from the top, the count * is incremented. If it then exits back through the top, the * same way it came in, the count is decremented and there is * no impact on the winding count. If, instead, the path exits * out the bottom, then the count is incremented again and a * full pass through the shadow is indicated by the winding count * having been incremented by 2. * * Thus, the winding count that it accumulates is actually double * the real winding count. Since the path is continuous, the * final answer should be a multiple of 2, otherwise there is a * logic error somewhere. * * If the path ever has a direct hit on the rectangle, then a * special value is returned. This special value terminates * all ongoing accumulation on up through the call chain and * ends up getting returned to the calling function which can * then produce an answer directly. For intersection tests, * the answer is always "true" if the path intersects the * rectangle. For containment tests, the answer is always * "false" if the path intersects the rectangle. Thus, no * further processing is ever needed if an intersection occurs. */ public static final int RECT_INTERSECTS = 0x80000000; /** * Accumulate the number of times the path crosses the shadow * extending to the right of the rectangle. See the comment * for the RECT_INTERSECTS constant for more complete details. * The return value is the sum of all crossings for both the * top and bottom of the shadow for every segment in the path, * or the special value RECT_INTERSECTS if the path ever enters * the interior of the rectangle. * The path must start with a SEG_MOVETO, otherwise an exception is * thrown. * The caller must check r[xy]{min,max} for NaN values. */ public static int rectCrossingsForPath(PathIterator pi, double rxmin, double rymin, double rxmax, double rymax) { if (rxmax <= rxmin || rymax <= rymin) { return 0; } if (pi.isDone()) { return 0; } double coords[] = new double[6]; if (pi.currentSegment(coords) != PathIterator.SEG_MOVETO) { throw new IllegalPathStateException("missing initial moveto "+ "in path definition"); } pi.next(); double curx, cury, movx, movy, endx, endy; curx = movx = coords[0]; cury = movy = coords[1]; int crossings = 0; while (crossings != RECT_INTERSECTS && !pi.isDone()) { switch (pi.currentSegment(coords)) { case PathIterator.SEG_MOVETO: if (curx != movx || cury != movy) { crossings = rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy); } // Count should always be a multiple of 2 here. // assert((crossings & 1) != 0); movx = curx = coords[0]; movy = cury = coords[1]; break; case PathIterator.SEG_LINETO: endx = coords[0]; endy = coords[1]; crossings = rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, endx, endy); curx = endx; cury = endy; break; case PathIterator.SEG_QUADTO: endx = coords[2]; endy = coords[3]; crossings = rectCrossingsForQuad(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[0], coords[1], endx, endy, 0); curx = endx; cury = endy; break; case PathIterator.SEG_CUBICTO: endx = coords[4]; endy = coords[5]; crossings = rectCrossingsForCubic(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[0], coords[1], coords[2], coords[3], endx, endy, 0); curx = endx; cury = endy; break; case PathIterator.SEG_CLOSE: if (curx != movx || cury != movy) { crossings = rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy); } curx = movx; cury = movy; // Count should always be a multiple of 2 here. // assert((crossings & 1) != 0); break; } pi.next(); } if (crossings != RECT_INTERSECTS && (curx != movx || cury != movy)) { crossings = rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy); } // Count should always be a multiple of 2 here. // assert((crossings & 1) != 0); return crossings; } /** * Accumulate the number of times the line crosses the shadow * extending to the right of the rectangle. See the comment * for the RECT_INTERSECTS constant for more complete details. */ public static int rectCrossingsForLine(int crossings, double rxmin, double rymin, double rxmax, double rymax, double x0, double y0, double x1, double y1) { if (y0 >= rymax && y1 >= rymax) return crossings; if (y0 <= rymin && y1 <= rymin) return crossings; if (x0 <= rxmin && x1 <= rxmin) return crossings; if (x0 >= rxmax && x1 >= rxmax) { // Line is entirely to the right of the rect // and the vertical ranges of the two overlap by a non-empty amount // Thus, this line segment is partially in the "right-shadow" // Path may have done a complete crossing // Or path may have entered or exited the right-shadow if (y0 < y1) { // y-increasing line segment... // We know that y0 < rymax and y1 > rymin if (y0 <= rymin) crossings++; if (y1 >= rymax) crossings++;