0N/A/*
2362N/A * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.awt.geom;
0N/A
0N/Aimport java.awt.geom.Rectangle2D;
0N/Aimport java.awt.geom.QuadCurve2D;
0N/Aimport java.awt.geom.CubicCurve2D;
0N/Aimport java.awt.geom.PathIterator;
0N/Aimport java.awt.geom.IllegalPathStateException;
0N/Aimport java.util.Vector;
0N/A
0N/Apublic abstract class Curve {
0N/A public static final int INCREASING = 1;
0N/A public static final int DECREASING = -1;
0N/A
0N/A protected int direction;
0N/A
0N/A public static void insertMove(Vector curves, double x, double y) {
0N/A curves.add(new Order0(x, y));
0N/A }
0N/A
0N/A public static void insertLine(Vector curves,
0N/A double x0, double y0,
0N/A double x1, double y1)
0N/A {
0N/A if (y0 < y1) {
0N/A curves.add(new Order1(x0, y0,
0N/A x1, y1,
0N/A INCREASING));
0N/A } else if (y0 > y1) {
0N/A curves.add(new Order1(x1, y1,
0N/A x0, y0,
0N/A DECREASING));
0N/A } else {
0N/A // Do not add horizontal lines
0N/A }
0N/A }
0N/A
0N/A public static void insertQuad(Vector curves,
0N/A double x0, double y0,
0N/A double coords[])
0N/A {
0N/A double y1 = coords[3];
0N/A if (y0 > y1) {
0N/A Order2.insert(curves, coords,
0N/A coords[2], y1,
0N/A coords[0], coords[1],
0N/A x0, y0,
0N/A DECREASING);
0N/A } else if (y0 == y1 && y0 == coords[1]) {
0N/A // Do not add horizontal lines
0N/A return;
0N/A } else {
0N/A Order2.insert(curves, coords,
0N/A x0, y0,
0N/A coords[0], coords[1],
0N/A coords[2], y1,
0N/A INCREASING);
0N/A }
0N/A }
0N/A
0N/A public static void insertCubic(Vector curves,
0N/A double x0, double y0,
0N/A double coords[])
0N/A {
0N/A double y1 = coords[5];
0N/A if (y0 > y1) {
0N/A Order3.insert(curves, coords,
0N/A coords[4], y1,
0N/A coords[2], coords[3],
0N/A coords[0], coords[1],
0N/A x0, y0,
0N/A DECREASING);
0N/A } else if (y0 == y1 && y0 == coords[1] && y0 == coords[3]) {
0N/A // Do not add horizontal lines
0N/A return;
0N/A } else {
0N/A Order3.insert(curves, coords,
0N/A x0, y0,
0N/A coords[0], coords[1],
0N/A coords[2], coords[3],
0N/A coords[4], y1,
0N/A INCREASING);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Calculates the number of times the given path
0N/A * crosses the ray extending to the right from (px,py).
0N/A * If the point lies on a part of the path,
0N/A * then no crossings are counted for that intersection.
0N/A * +1 is added for each crossing where the Y coordinate is increasing
0N/A * -1 is added for each crossing where the Y coordinate is decreasing
0N/A * The return value is the sum of all crossings for every segment in
0N/A * the path.
0N/A * The path must start with a SEG_MOVETO, otherwise an exception is
0N/A * thrown.
0N/A * The caller must check p[xy] for NaN values.
0N/A * The caller may also reject infinite p[xy] values as well.
0N/A */
0N/A public static int pointCrossingsForPath(PathIterator pi,
0N/A double px, double py)
0N/A {
0N/A if (pi.isDone()) {
0N/A return 0;
0N/A }
0N/A double coords[] = new double[6];
0N/A if (pi.currentSegment(coords) != PathIterator.SEG_MOVETO) {
0N/A throw new IllegalPathStateException("missing initial moveto "+
0N/A "in path definition");
0N/A }
0N/A pi.next();
0N/A double movx = coords[0];
0N/A double movy = coords[1];
0N/A double curx = movx;
0N/A double cury = movy;
0N/A double endx, endy;
0N/A int crossings = 0;
0N/A while (!pi.isDone()) {
0N/A switch (pi.currentSegment(coords)) {
0N/A case PathIterator.SEG_MOVETO:
0N/A if (cury != movy) {
0N/A crossings += pointCrossingsForLine(px, py,
0N/A curx, cury,
0N/A movx, movy);
0N/A }
0N/A movx = curx = coords[0];
0N/A movy = cury = coords[1];
0N/A break;
0N/A case PathIterator.SEG_LINETO:
0N/A endx = coords[0];
0N/A endy = coords[1];
0N/A crossings += pointCrossingsForLine(px, py,
0N/A curx, cury,
0N/A endx, endy);
0N/A curx = endx;
0N/A cury = endy;
0N/A break;
0N/A case PathIterator.SEG_QUADTO:
0N/A endx = coords[2];
0N/A endy = coords[3];
0N/A crossings += pointCrossingsForQuad(px, py,
0N/A curx, cury,
0N/A coords[0], coords[1],
0N/A endx, endy, 0);
0N/A curx = endx;
0N/A cury = endy;
0N/A break;
0N/A case PathIterator.SEG_CUBICTO:
0N/A endx = coords[4];
0N/A endy = coords[5];
0N/A crossings += pointCrossingsForCubic(px, py,
0N/A curx, cury,
0N/A coords[0], coords[1],
0N/A coords[2], coords[3],
0N/A endx, endy, 0);
0N/A curx = endx;
0N/A cury = endy;
0N/A break;
0N/A case PathIterator.SEG_CLOSE:
0N/A if (cury != movy) {
0N/A crossings += pointCrossingsForLine(px, py,
0N/A curx, cury,
0N/A movx, movy);
0N/A }
0N/A curx = movx;
0N/A cury = movy;
0N/A break;
0N/A }
0N/A pi.next();
0N/A }
0N/A if (cury != movy) {
0N/A crossings += pointCrossingsForLine(px, py,
0N/A curx, cury,
0N/A movx, movy);
0N/A }
0N/A return crossings;
0N/A }
0N/A
0N/A /**
0N/A * Calculates the number of times the line from (x0,y0) to (x1,y1)
0N/A * crosses the ray extending to the right from (px,py).
0N/A * If the point lies on the line, then no crossings are recorded.
0N/A * +1 is returned for a crossing where the Y coordinate is increasing
0N/A * -1 is returned for a crossing where the Y coordinate is decreasing
0N/A */
0N/A public static int pointCrossingsForLine(double px, double py,
0N/A double x0, double y0,
0N/A double x1, double y1)
0N/A {
0N/A if (py < y0 && py < y1) return 0;
0N/A if (py >= y0 && py >= y1) return 0;
0N/A // assert(y0 != y1);
0N/A if (px >= x0 && px >= x1) return 0;
0N/A if (px < x0 && px < x1) return (y0 < y1) ? 1 : -1;
0N/A double xintercept = x0 + (py - y0) * (x1 - x0) / (y1 - y0);
0N/A if (px >= xintercept) return 0;
0N/A return (y0 < y1) ? 1 : -1;
0N/A }
0N/A
0N/A /**
0N/A * Calculates the number of times the quad from (x0,y0) to (x1,y1)
0N/A * crosses the ray extending to the right from (px,py).
0N/A * If the point lies on a part of the curve,
0N/A * then no crossings are counted for that intersection.
0N/A * the level parameter should be 0 at the top-level call and will count
0N/A * up for each recursion level to prevent infinite recursion
0N/A * +1 is added for each crossing where the Y coordinate is increasing
0N/A * -1 is added for each crossing where the Y coordinate is decreasing
0N/A */
0N/A public static int pointCrossingsForQuad(double px, double py,
0N/A double x0, double y0,
0N/A double xc, double yc,
0N/A double x1, double y1, int level)
0N/A {
0N/A if (py < y0 && py < yc && py < y1) return 0;
0N/A if (py >= y0 && py >= yc && py >= y1) return 0;
0N/A // Note y0 could equal y1...
0N/A if (px >= x0 && px >= xc && px >= x1) return 0;
0N/A if (px < x0 && px < xc && px < x1) {
0N/A if (py >= y0) {
0N/A if (py < y1) return 1;
0N/A } else {
0N/A // py < y0
0N/A if (py >= y1) return -1;
0N/A }
0N/A // py outside of y01 range, and/or y0==y1
0N/A return 0;
0N/A }
0N/A // double precision only has 52 bits of mantissa
0N/A if (level > 52) return pointCrossingsForLine(px, py, x0, y0, x1, y1);
0N/A double x0c = (x0 + xc) / 2;
0N/A double y0c = (y0 + yc) / 2;
0N/A double xc1 = (xc + x1) / 2;
0N/A double yc1 = (yc + y1) / 2;
0N/A xc = (x0c + xc1) / 2;
0N/A yc = (y0c + yc1) / 2;
0N/A if (Double.isNaN(xc) || Double.isNaN(yc)) {
0N/A // [xy]c are NaN if any of [xy]0c or [xy]c1 are NaN
0N/A // [xy]0c or [xy]c1 are NaN if any of [xy][0c1] are NaN
0N/A // These values are also NaN if opposing infinities are added
0N/A return 0;
0N/A }
0N/A return (pointCrossingsForQuad(px, py,
0N/A x0, y0, x0c, y0c, xc, yc,
0N/A level+1) +
0N/A pointCrossingsForQuad(px, py,
0N/A xc, yc, xc1, yc1, x1, y1,
0N/A level+1));
0N/A }
0N/A
0N/A /**
0N/A * Calculates the number of times the cubic from (x0,y0) to (x1,y1)
0N/A * crosses the ray extending to the right from (px,py).
0N/A * If the point lies on a part of the curve,
0N/A * then no crossings are counted for that intersection.
0N/A * the level parameter should be 0 at the top-level call and will count
0N/A * up for each recursion level to prevent infinite recursion
0N/A * +1 is added for each crossing where the Y coordinate is increasing
0N/A * -1 is added for each crossing where the Y coordinate is decreasing
0N/A */
0N/A public static int pointCrossingsForCubic(double px, double py,
0N/A double x0, double y0,
0N/A double xc0, double yc0,
0N/A double xc1, double yc1,
0N/A double x1, double y1, int level)
0N/A {
0N/A if (py < y0 && py < yc0 && py < yc1 && py < y1) return 0;
0N/A if (py >= y0 && py >= yc0 && py >= yc1 && py >= y1) return 0;
0N/A // Note y0 could equal yc0...
0N/A if (px >= x0 && px >= xc0 && px >= xc1 && px >= x1) return 0;
0N/A if (px < x0 && px < xc0 && px < xc1 && px < x1) {
0N/A if (py >= y0) {
0N/A if (py < y1) return 1;
0N/A } else {
0N/A // py < y0
0N/A if (py >= y1) return -1;
0N/A }
0N/A // py outside of y01 range, and/or y0==yc0
0N/A return 0;
0N/A }
0N/A // double precision only has 52 bits of mantissa
0N/A if (level > 52) return pointCrossingsForLine(px, py, x0, y0, x1, y1);
0N/A double xmid = (xc0 + xc1) / 2;
0N/A double ymid = (yc0 + yc1) / 2;
0N/A xc0 = (x0 + xc0) / 2;
0N/A yc0 = (y0 + yc0) / 2;
0N/A xc1 = (xc1 + x1) / 2;
0N/A yc1 = (yc1 + y1) / 2;
0N/A double xc0m = (xc0 + xmid) / 2;
0N/A double yc0m = (yc0 + ymid) / 2;
0N/A double xmc1 = (xmid + xc1) / 2;
0N/A double ymc1 = (ymid + yc1) / 2;
0N/A xmid = (xc0m + xmc1) / 2;
0N/A ymid = (yc0m + ymc1) / 2;
0N/A if (Double.isNaN(xmid) || Double.isNaN(ymid)) {
0N/A // [xy]mid are NaN if any of [xy]c0m or [xy]mc1 are NaN
0N/A // [xy]c0m or [xy]mc1 are NaN if any of [xy][c][01] are NaN
0N/A // These values are also NaN if opposing infinities are added
0N/A return 0;
0N/A }
0N/A return (pointCrossingsForCubic(px, py,
0N/A x0, y0, xc0, yc0,
0N/A xc0m, yc0m, xmid, ymid, level+1) +
0N/A pointCrossingsForCubic(px, py,
0N/A xmid, ymid, xmc1, ymc1,
0N/A xc1, yc1, x1, y1, level+1));
0N/A }
0N/A
0N/A /**
0N/A * The rectangle intersection test counts the number of times
0N/A * that the path crosses through the shadow that the rectangle
0N/A * projects to the right towards (x => +INFINITY).
0N/A *
0N/A * During processing of the path it actually counts every time
0N/A * the path crosses either or both of the top and bottom edges
0N/A * of that shadow. If the path enters from the top, the count
0N/A * is incremented. If it then exits back through the top, the
0N/A * same way it came in, the count is decremented and there is
0N/A * no impact on the winding count. If, instead, the path exits
0N/A * out the bottom, then the count is incremented again and a
0N/A * full pass through the shadow is indicated by the winding count
0N/A * having been incremented by 2.
0N/A *
0N/A * Thus, the winding count that it accumulates is actually double
0N/A * the real winding count. Since the path is continuous, the
0N/A * final answer should be a multiple of 2, otherwise there is a
0N/A * logic error somewhere.
0N/A *
0N/A * If the path ever has a direct hit on the rectangle, then a
0N/A * special value is returned. This special value terminates
0N/A * all ongoing accumulation on up through the call chain and
0N/A * ends up getting returned to the calling function which can
0N/A * then produce an answer directly. For intersection tests,
0N/A * the answer is always "true" if the path intersects the
0N/A * rectangle. For containment tests, the answer is always
0N/A * "false" if the path intersects the rectangle. Thus, no
0N/A * further processing is ever needed if an intersection occurs.
0N/A */
0N/A public static final int RECT_INTERSECTS = 0x80000000;
0N/A
0N/A /**
0N/A * Accumulate the number of times the path crosses the shadow
0N/A * extending to the right of the rectangle. See the comment
0N/A * for the RECT_INTERSECTS constant for more complete details.
0N/A * The return value is the sum of all crossings for both the
0N/A * top and bottom of the shadow for every segment in the path,
0N/A * or the special value RECT_INTERSECTS if the path ever enters
0N/A * the interior of the rectangle.
0N/A * The path must start with a SEG_MOVETO, otherwise an exception is
0N/A * thrown.
0N/A * The caller must check r[xy]{min,max} for NaN values.
0N/A */
0N/A public static int rectCrossingsForPath(PathIterator pi,
0N/A double rxmin, double rymin,
0N/A double rxmax, double rymax)
0N/A {
0N/A if (rxmax <= rxmin || rymax <= rymin) {
0N/A return 0;
0N/A }
0N/A if (pi.isDone()) {
0N/A return 0;
0N/A }
0N/A double coords[] = new double[6];
0N/A if (pi.currentSegment(coords) != PathIterator.SEG_MOVETO) {
0N/A throw new IllegalPathStateException("missing initial moveto "+
0N/A "in path definition");
0N/A }
0N/A pi.next();
0N/A double curx, cury, movx, movy, endx, endy;
0N/A curx = movx = coords[0];
0N/A cury = movy = coords[1];
0N/A int crossings = 0;
0N/A while (crossings != RECT_INTERSECTS && !pi.isDone()) {
0N/A switch (pi.currentSegment(coords)) {
0N/A case PathIterator.SEG_MOVETO:
0N/A if (curx != movx || cury != movy) {
0N/A crossings = rectCrossingsForLine(crossings,
0N/A rxmin, rymin,
0N/A rxmax, rymax,
0N/A curx, cury,
0N/A movx, movy);
0N/A }
0N/A // Count should always be a multiple of 2 here.
0N/A // assert((crossings & 1) != 0);
0N/A movx = curx = coords[0];
0N/A movy = cury = coords[1];
0N/A break;
0N/A case PathIterator.SEG_LINETO:
0N/A endx = coords[0];
0N/A endy = coords[1];
0N/A crossings = rectCrossingsForLine(crossings,
0N/A rxmin, rymin,
0N/A rxmax, rymax,
0N/A curx, cury,
0N/A endx, endy);
0N/A curx = endx;
0N/A cury = endy;
0N/A break;
0N/A case PathIterator.SEG_QUADTO:
0N/A endx = coords[2];
0N/A endy = coords[3];
0N/A crossings = rectCrossingsForQuad(crossings,
0N/A rxmin, rymin,
0N/A rxmax, rymax,
0N/A curx, cury,
0N/A coords[0], coords[1],
0N/A endx, endy, 0);
0N/A curx = endx;
0N/A cury = endy;
0N/A break;
0N/A case PathIterator.SEG_CUBICTO:
0N/A endx = coords[4];
0N/A endy = coords[5];
0N/A crossings = rectCrossingsForCubic(crossings,
0N/A rxmin, rymin,
0N/A rxmax, rymax,
0N/A curx, cury,
0N/A coords[0], coords[1],
0N/A coords[2], coords[3],
0N/A endx, endy, 0);
0N/A curx = endx;
0N/A cury = endy;
0N/A break;
0N/A case PathIterator.SEG_CLOSE:
0N/A if (curx != movx || cury != movy) {
0N/A crossings = rectCrossingsForLine(crossings,
0N/A rxmin, rymin,
0N/A rxmax, rymax,
0N/A curx, cury,
0N/A movx, movy);
0N/A }
0N/A curx = movx;
0N/A cury = movy;
0N/A // Count should always be a multiple of 2 here.
0N/A // assert((crossings & 1) != 0);
0N/A break;
0N/A }
0N/A pi.next();
0N/A }
0N/A if (crossings != RECT_INTERSECTS && (curx != movx || cury != movy)) {
0N/A crossings = rectCrossingsForLine(crossings,
0N/A rxmin, rymin,
0N/A rxmax, rymax,
0N/A curx, cury,
0N/A movx, movy);
0N/A }
0N/A // Count should always be a multiple of 2 here.
0N/A // assert((crossings & 1) != 0);
0N/A return crossings;
0N/A }
0N/A
0N/A /**
0N/A * Accumulate the number of times the line crosses the shadow
0N/A * extending to the right of the rectangle. See the comment
0N/A * for the RECT_INTERSECTS constant for more complete details.
0N/A */
0N/A public static int rectCrossingsForLine(int crossings,
0N/A double rxmin, double rymin,
0N/A double rxmax, double rymax,
0N/A double x0, double y0,
0N/A double x1, double y1)
0N/A {
0N/A if (y0 >= rymax && y1 >= rymax) return crossings;
0N/A if (y0 <= rymin && y1 <= rymin) return crossings;
0N/A if (x0 <= rxmin && x1 <= rxmin) return crossings;
0N/A if (x0 >= rxmax && x1 >= rxmax) {
0N/A // Line is entirely to the right of the rect
0N/A // and the vertical ranges of the two overlap by a non-empty amount
0N/A // Thus, this line segment is partially in the "right-shadow"
0N/A // Path may have done a complete crossing
0N/A // Or path may have entered or exited the right-shadow
0N/A if (y0 < y1) {
0N/A // y-increasing line segment...
0N/A // We know that y0 < rymax and y1 > rymin
0N/A if (y0 <= rymin) crossings++;
0N/A if (y1 >= rymax) crossings++;
0N/A } else if (y1 < y0) {
0N/A // y-decreasing line segment...
0N/A // We know that y1 < rymax and y0 > rymin
0N/A if (y1 <= rymin) crossings--;
0N/A if (y0 >= rymax) crossings--;
0N/A }
0N/A return crossings;
0N/A }
0N/A // Remaining case:
0N/A // Both x and y ranges overlap by a non-empty amount
0N/A // First do trivial INTERSECTS rejection of the cases
0N/A // where one of the endpoints is inside the rectangle.
0N/A if ((x0 > rxmin && x0 < rxmax && y0 > rymin && y0 < rymax) ||
0N/A (x1 > rxmin && x1 < rxmax && y1 > rymin && y1 < rymax))
0N/A {
0N/A return RECT_INTERSECTS;
0N/A }
0N/A // Otherwise calculate the y intercepts and see where
0N/A // they fall with respect to the rectangle
0N/A double xi0 = x0;
0N/A if (y0 < rymin) {
0N/A xi0 += ((rymin - y0) * (x1 - x0) / (y1 - y0));
0N/A } else if (y0 > rymax) {
0N/A xi0 += ((rymax - y0) * (x1 - x0) / (y1 - y0));
0N/A }
0N/A double xi1 = x1;
0N/A if (y1 < rymin) {
0N/A xi1 += ((rymin - y1) * (x0 - x1) / (y0 - y1));
0N/A } else if (y1 > rymax) {
0N/A xi1 += ((rymax - y1) * (x0 - x1) / (y0 - y1));
0N/A }
0N/A if (xi0 <= rxmin && xi1 <= rxmin) return crossings;
0N/A if (xi0 >= rxmax && xi1 >= rxmax) {
0N/A if (y0 < y1) {
0N/A // y-increasing line segment...
0N/A // We know that y0 < rymax and y1 > rymin
0N/A if (y0 <= rymin) crossings++;
0N/A if (y1 >= rymax) crossings++;
0N/A } else if (y1 < y0) {
0N/A // y-decreasing line segment...
0N/A // We know that y1 < rymax and y0 > rymin
0N/A if (y1 <= rymin) crossings--;
0N/A if (y0 >= rymax) crossings--;
0N/A }
0N/A return crossings;
0N/A }
0N/A return RECT_INTERSECTS;
0N/A }
0N/A
0N/A /**
0N/A * Accumulate the number of times the quad crosses the shadow
0N/A * extending to the right of the rectangle. See the comment
0N/A * for the RECT_INTERSECTS constant for more complete details.
0N/A */
0N/A public static int rectCrossingsForQuad(int crossings,
0N/A double rxmin, double rymin,
0N/A double rxmax, double rymax,
0N/A double x0, double y0,
0N/A double xc, double yc,
0N/A double x1, double y1,
0N/A int level)
0N/A {
0N/A if (y0 >= rymax && yc >= rymax && y1 >= rymax) return crossings;
0N/A if (y0 <= rymin && yc <= rymin && y1 <= rymin) return crossings;
0N/A if (x0 <= rxmin && xc <= rxmin && x1 <= rxmin) return crossings;
0N/A if (x0 >= rxmax && xc >= rxmax && x1 >= rxmax) {
0N/A // Quad is entirely to the right of the rect
0N/A // and the vertical range of the 3 Y coordinates of the quad
0N/A // overlaps the vertical range of the rect by a non-empty amount
0N/A // We now judge the crossings solely based on the line segment
0N/A // connecting the endpoints of the quad.
0N/A // Note that we may have 0, 1, or 2 crossings as the control
0N/A // point may be causing the Y range intersection while the
0N/A // two endpoints are entirely above or below.
0N/A if (y0 < y1) {
0N/A // y-increasing line segment...
0N/A if (y0 <= rymin && y1 > rymin) crossings++;
0N/A if (y0 < rymax && y1 >= rymax) crossings++;
0N/A } else if (y1 < y0) {
0N/A // y-decreasing line segment...
0N/A if (y1 <= rymin && y0 > rymin) crossings--;
0N/A if (y1 < rymax && y0 >= rymax) crossings--;
0N/A }
0N/A return crossings;
0N/A }
0N/A // The intersection of ranges is more complicated
0N/A // First do trivial INTERSECTS rejection of the cases
0N/A // where one of the endpoints is inside the rectangle.
0N/A if ((x0 < rxmax && x0 > rxmin && y0 < rymax && y0 > rymin) ||
0N/A (x1 < rxmax && x1 > rxmin && y1 < rymax && y1 > rymin))
0N/A {
0N/A return RECT_INTERSECTS;
0N/A }
0N/A // Otherwise, subdivide and look for one of the cases above.
0N/A // double precision only has 52 bits of mantissa
0N/A if (level > 52) {
0N/A return rectCrossingsForLine(crossings,
0N/A rxmin, rymin, rxmax, rymax,
0N/A x0, y0, x1, y1);
0N/A }
0N/A double x0c = (x0 + xc) / 2;
0N/A double y0c = (y0 + yc) / 2;
0N/A double xc1 = (xc + x1) / 2;
0N/A double yc1 = (yc + y1) / 2;
0N/A xc = (x0c + xc1) / 2;
0N/A yc = (y0c + yc1) / 2;
0N/A if (Double.isNaN(xc) || Double.isNaN(yc)) {
0N/A // [xy]c are NaN if any of [xy]0c or [xy]c1 are NaN
0N/A // [xy]0c or [xy]c1 are NaN if any of [xy][0c1] are NaN
0N/A // These values are also NaN if opposing infinities are added
0N/A return 0;
0N/A }
0N/A crossings = rectCrossingsForQuad(crossings,
0N/A rxmin, rymin, rxmax, rymax,
0N/A x0, y0, x0c, y0c, xc, yc,
0N/A level+1);
0N/A if (crossings != RECT_INTERSECTS) {
0N/A crossings = rectCrossingsForQuad(crossings,
0N/A rxmin, rymin, rxmax, rymax,
0N/A xc, yc, xc1, yc1, x1, y1,
0N/A level+1);
0N/A }
0N/A return crossings;
0N/A }
0N/A
0N/A /**
0N/A * Accumulate the number of times the cubic crosses the shadow
0N/A * extending to the right of the rectangle. See the comment
0N/A * for the RECT_INTERSECTS constant for more complete details.
0N/A */
0N/A public static int rectCrossingsForCubic(int crossings,
0N/A double rxmin, double rymin,
0N/A double rxmax, double rymax,
0N/A double x0, double y0,
0N/A double xc0, double yc0,
0N/A double xc1, double yc1,
0N/A double x1, double y1,
0N/A int level)
0N/A {
0N/A if (y0 >= rymax && yc0 >= rymax && yc1 >= rymax && y1 >= rymax) {
0N/A return crossings;
0N/A }
0N/A if (y0 <= rymin && yc0 <= rymin && yc1 <= rymin && y1 <= rymin) {
0N/A return crossings;
0N/A }
0N/A if (x0 <= rxmin && xc0 <= rxmin && xc1 <= rxmin && x1 <= rxmin) {
0N/A return crossings;
0N/A }
0N/A if (x0 >= rxmax && xc0 >= rxmax && xc1 >= rxmax && x1 >= rxmax) {
0N/A // Cubic is entirely to the right of the rect
0N/A // and the vertical range of the 4 Y coordinates of the cubic
0N/A // overlaps the vertical range of the rect by a non-empty amount
0N/A // We now judge the crossings solely based on the line segment
0N/A // connecting the endpoints of the cubic.
0N/A // Note that we may have 0, 1, or 2 crossings as the control
0N/A // points may be causing the Y range intersection while the
0N/A // two endpoints are entirely above or below.
0N/A if (y0 < y1) {
0N/A // y-increasing line segment...
0N/A if (y0 <= rymin && y1 > rymin) crossings++;
0N/A if (y0 < rymax && y1 >= rymax) crossings++;
0N/A } else if (y1 < y0) {
0N/A // y-decreasing line segment...
0N/A if (y1 <= rymin && y0 > rymin) crossings--;
0N/A if (y1 < rymax && y0 >= rymax) crossings--;
0N/A }
0N/A return crossings;
0N/A }
0N/A // The intersection of ranges is more complicated
0N/A // First do trivial INTERSECTS rejection of the cases
0N/A // where one of the endpoints is inside the rectangle.
0N/A if ((x0 > rxmin && x0 < rxmax && y0 > rymin && y0 < rymax) ||
0N/A (x1 > rxmin && x1 < rxmax && y1 > rymin && y1 < rymax))
0N/A {
0N/A return RECT_INTERSECTS;
0N/A }
0N/A // Otherwise, subdivide and look for one of the cases above.
0N/A // double precision only has 52 bits of mantissa
0N/A if (level > 52) {
0N/A return rectCrossingsForLine(crossings,
0N/A rxmin, rymin, rxmax, rymax,
0N/A x0, y0, x1, y1);
0N/A }
0N/A double xmid = (xc0 + xc1) / 2;
0N/A double ymid = (yc0 + yc1) / 2;
0N/A xc0 = (x0 + xc0) / 2;
0N/A yc0 = (y0 + yc0) / 2;
0N/A xc1 = (xc1 + x1) / 2;
0N/A yc1 = (yc1 + y1) / 2;
0N/A double xc0m = (xc0 + xmid) / 2;
0N/A double yc0m = (yc0 + ymid) / 2;
0N/A double xmc1 = (xmid + xc1) / 2;
0N/A double ymc1 = (ymid + yc1) / 2;
0N/A xmid = (xc0m + xmc1) / 2;
0N/A ymid = (yc0m + ymc1) / 2;
0N/A if (Double.isNaN(xmid) || Double.isNaN(ymid)) {
0N/A // [xy]mid are NaN if any of [xy]c0m or [xy]mc1 are NaN
0N/A // [xy]c0m or [xy]mc1 are NaN if any of [xy][c][01] are NaN
0N/A // These values are also NaN if opposing infinities are added
0N/A return 0;
0N/A }
0N/A crossings = rectCrossingsForCubic(crossings,
0N/A rxmin, rymin, rxmax, rymax,
0N/A x0, y0, xc0, yc0,
0N/A xc0m, yc0m, xmid, ymid, level+1);
0N/A if (crossings != RECT_INTERSECTS) {
0N/A crossings = rectCrossingsForCubic(crossings,
0N/A rxmin, rymin, rxmax, rymax,
0N/A xmid, ymid, xmc1, ymc1,
0N/A xc1, yc1, x1, y1, level+1);
0N/A }
0N/A return crossings;
0N/A }
0N/A
0N/A public Curve(int direction) {
0N/A this.direction = direction;
0N/A }
0N/A
0N/A public final int getDirection() {
0N/A return direction;
0N/A }
0N/A
0N/A public final Curve getWithDirection(int direction) {
0N/A return (this.direction == direction ? this : getReversedCurve());
0N/A }
0N/A
0N/A public static double round(double v) {
0N/A //return Math.rint(v*10)/10;
0N/A return v;
0N/A }
0N/A
0N/A public static int orderof(double x1, double x2) {
0N/A if (x1 < x2) {
0N/A return -1;
0N/A }
0N/A if (x1 > x2) {
0N/A return 1;
0N/A }
0N/A return 0;
0N/A }
0N/A
0N/A public static long signeddiffbits(double y1, double y2) {
0N/A return (Double.doubleToLongBits(y1) - Double.doubleToLongBits(y2));
0N/A }
0N/A public static long diffbits(double y1, double y2) {
0N/A return Math.abs(Double.doubleToLongBits(y1) -
0N/A Double.doubleToLongBits(y2));
0N/A }
0N/A public static double prev(double v) {
0N/A return Double.longBitsToDouble(Double.doubleToLongBits(v)-1);
0N/A }
0N/A public static double next(double v) {
0N/A return Double.longBitsToDouble(Double.doubleToLongBits(v)+1);
0N/A }
0N/A
0N/A public String toString() {
0N/A return ("Curve["+
0N/A getOrder()+", "+
0N/A ("("+round(getX0())+", "+round(getY0())+"), ")+
0N/A controlPointString()+
0N/A ("("+round(getX1())+", "+round(getY1())+"), ")+
0N/A (direction == INCREASING ? "D" : "U")+
0N/A "]");
0N/A }
0N/A
0N/A public String controlPointString() {
0N/A return "";
0N/A }
0N/A
0N/A public abstract int getOrder();
0N/A
0N/A public abstract double getXTop();
0N/A public abstract double getYTop();
0N/A public abstract double getXBot();
0N/A public abstract double getYBot();
0N/A
0N/A public abstract double getXMin();
0N/A public abstract double getXMax();
0N/A
0N/A public abstract double getX0();
0N/A public abstract double getY0();
0N/A public abstract double getX1();
0N/A public abstract double getY1();
0N/A
0N/A public abstract double XforY(double y);
0N/A public abstract double TforY(double y);
0N/A public abstract double XforT(double t);
0N/A public abstract double YforT(double t);
0N/A public abstract double dXforT(double t, int deriv);
0N/A public abstract double dYforT(double t, int deriv);
0N/A
0N/A public abstract double nextVertical(double t0, double t1);
0N/A
0N/A public int crossingsFor(double x, double y) {
0N/A if (y >= getYTop() && y < getYBot()) {
0N/A if (x < getXMax() && (x < getXMin() || x < XforY(y))) {
0N/A return 1;
0N/A }
0N/A }
0N/A return 0;
0N/A }
0N/A
0N/A public boolean accumulateCrossings(Crossings c) {
0N/A double xhi = c.getXHi();
0N/A if (getXMin() >= xhi) {
0N/A return false;
0N/A }
0N/A double xlo = c.getXLo();
0N/A double ylo = c.getYLo();
0N/A double yhi = c.getYHi();
0N/A double y0 = getYTop();
0N/A double y1 = getYBot();
0N/A double tstart, ystart, tend, yend;
0N/A if (y0 < ylo) {
0N/A if (y1 <= ylo) {
0N/A return false;
0N/A }
0N/A ystart = ylo;
0N/A tstart = TforY(ylo);
0N/A } else {
0N/A if (y0 >= yhi) {
0N/A return false;
0N/A }
0N/A ystart = y0;
0N/A tstart = 0;
0N/A }
0N/A if (y1 > yhi) {
0N/A yend = yhi;
0N/A tend = TforY(yhi);
0N/A } else {
0N/A yend = y1;
0N/A tend = 1;
0N/A }
0N/A boolean hitLo = false;
0N/A boolean hitHi = false;
0N/A while (true) {
0N/A double x = XforT(tstart);
0N/A if (x < xhi) {
0N/A if (hitHi || x > xlo) {
0N/A return true;
0N/A }
0N/A hitLo = true;
0N/A } else {
0N/A if (hitLo) {
0N/A return true;
0N/A }
0N/A hitHi = true;
0N/A }
0N/A if (tstart >= tend) {
0N/A break;
0N/A }
0N/A tstart = nextVertical(tstart, tend);
0N/A }
0N/A if (hitLo) {
0N/A c.record(ystart, yend, direction);
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A public abstract void enlarge(Rectangle2D r);
0N/A
0N/A public Curve getSubCurve(double ystart, double yend) {
0N/A return getSubCurve(ystart, yend, direction);
0N/A }
0N/A
0N/A public abstract Curve getReversedCurve();
0N/A public abstract Curve getSubCurve(double ystart, double yend, int dir);
0N/A
0N/A public int compareTo(Curve that, double yrange[]) {
0N/A /*
0N/A System.out.println(this+".compareTo("+that+")");
0N/A System.out.println("target range = "+yrange[0]+"=>"+yrange[1]);
0N/A */
0N/A double y0 = yrange[0];
0N/A double y1 = yrange[1];
0N/A y1 = Math.min(Math.min(y1, this.getYBot()), that.getYBot());
0N/A if (y1 <= yrange[0]) {
0N/A System.err.println("this == "+this);
0N/A System.err.println("that == "+that);
0N/A System.out.println("target range = "+yrange[0]+"=>"+yrange[1]);
0N/A throw new InternalError("backstepping from "+yrange[0]+" to "+y1);
0N/A }
0N/A yrange[1] = y1;
0N/A if (this.getXMax() <= that.getXMin()) {
0N/A if (this.getXMin() == that.getXMax()) {
0N/A return 0;
0N/A }
0N/A return -1;
0N/A }
0N/A if (this.getXMin() >= that.getXMax()) {
0N/A return 1;
0N/A }
0N/A // Parameter s for thi(s) curve and t for tha(t) curve
0N/A // [st]0 = parameters for top of current section of interest
0N/A // [st]1 = parameters for bottom of valid range
0N/A // [st]h = parameters for hypothesis point
0N/A // [d][xy]s = valuations of thi(s) curve at sh
0N/A // [d][xy]t = valuations of tha(t) curve at th
0N/A double s0 = this.TforY(y0);
0N/A double ys0 = this.YforT(s0);
0N/A if (ys0 < y0) {
0N/A s0 = refineTforY(s0, ys0, y0);
0N/A ys0 = this.YforT(s0);
0N/A }
0N/A double s1 = this.TforY(y1);
0N/A if (this.YforT(s1) < y0) {
0N/A s1 = refineTforY(s1, this.YforT(s1), y0);
0N/A //System.out.println("s1 problem!");
0N/A }
0N/A double t0 = that.TforY(y0);
0N/A double yt0 = that.YforT(t0);
0N/A if (yt0 < y0) {
0N/A t0 = that.refineTforY(t0, yt0, y0);
0N/A yt0 = that.YforT(t0);
0N/A }
0N/A double t1 = that.TforY(y1);
0N/A if (that.YforT(t1) < y0) {
0N/A t1 = that.refineTforY(t1, that.YforT(t1), y0);
0N/A //System.out.println("t1 problem!");
0N/A }
0N/A double xs0 = this.XforT(s0);
0N/A double xt0 = that.XforT(t0);
0N/A double scale = Math.max(Math.abs(y0), Math.abs(y1));
0N/A double ymin = Math.max(scale * 1E-14, 1E-300);
0N/A if (fairlyClose(xs0, xt0)) {
0N/A double bump = ymin;
0N/A double maxbump = Math.min(ymin * 1E13, (y1 - y0) * .1);
0N/A double y = y0 + bump;
0N/A while (y <= y1) {
0N/A if (fairlyClose(this.XforY(y), that.XforY(y))) {
0N/A if ((bump *= 2) > maxbump) {
0N/A bump = maxbump;
0N/A }
0N/A } else {
0N/A y -= bump;
0N/A while (true) {
0N/A bump /= 2;
0N/A double newy = y + bump;
0N/A if (newy <= y) {
0N/A break;
0N/A }
0N/A if (fairlyClose(this.XforY(newy), that.XforY(newy))) {
0N/A y = newy;
0N/A }
0N/A }
0N/A break;
0N/A }
0N/A y += bump;
0N/A }
0N/A if (y > y0) {
0N/A if (y < y1) {
0N/A yrange[1] = y;
0N/A }
0N/A return 0;
0N/A }
0N/A }
0N/A //double ymin = y1 * 1E-14;
0N/A if (ymin <= 0) {
0N/A System.out.println("ymin = "+ymin);
0N/A }
0N/A /*
0N/A System.out.println("s range = "+s0+" to "+s1);
0N/A System.out.println("t range = "+t0+" to "+t1);
0N/A */
0N/A while (s0 < s1 && t0 < t1) {
0N/A double sh = this.nextVertical(s0, s1);
0N/A double xsh = this.XforT(sh);
0N/A double ysh = this.YforT(sh);
0N/A double th = that.nextVertical(t0, t1);
0N/A double xth = that.XforT(th);
0N/A double yth = that.YforT(th);
0N/A /*
0N/A System.out.println("sh = "+sh);
0N/A System.out.println("th = "+th);
0N/A */
0N/A try {
0N/A if (findIntersect(that, yrange, ymin, 0, 0,
0N/A s0, xs0, ys0, sh, xsh, ysh,
0N/A t0, xt0, yt0, th, xth, yth)) {
0N/A break;
0N/A }
0N/A } catch (Throwable t) {
0N/A System.err.println("Error: "+t);
0N/A System.err.println("y range was "+yrange[0]+"=>"+yrange[1]);
0N/A System.err.println("s y range is "+ys0+"=>"+ysh);
0N/A System.err.println("t y range is "+yt0+"=>"+yth);
0N/A System.err.println("ymin is "+ymin);
0N/A return 0;
0N/A }
0N/A if (ysh < yth) {
0N/A if (ysh > yrange[0]) {
0N/A if (ysh < yrange[1]) {
0N/A yrange[1] = ysh;
0N/A }
0N/A break;
0N/A }
0N/A s0 = sh;
0N/A xs0 = xsh;
0N/A ys0 = ysh;
0N/A } else {
0N/A if (yth > yrange[0]) {
0N/A if (yth < yrange[1]) {
0N/A yrange[1] = yth;
0N/A }
0N/A break;
0N/A }
0N/A t0 = th;
0N/A xt0 = xth;
0N/A yt0 = yth;
0N/A }
0N/A }
0N/A double ymid = (yrange[0] + yrange[1]) / 2;
0N/A /*
0N/A System.out.println("final this["+s0+", "+sh+", "+s1+"]");
0N/A System.out.println("final y["+ys0+", "+ysh+"]");
0N/A System.out.println("final that["+t0+", "+th+", "+t1+"]");
0N/A System.out.println("final y["+yt0+", "+yth+"]");
0N/A System.out.println("final order = "+orderof(this.XforY(ymid),
0N/A that.XforY(ymid)));
0N/A System.out.println("final range = "+yrange[0]+"=>"+yrange[1]);
0N/A */
0N/A /*
0N/A System.out.println("final sx = "+this.XforY(ymid));
0N/A System.out.println("final tx = "+that.XforY(ymid));
0N/A System.out.println("final order = "+orderof(this.XforY(ymid),
0N/A that.XforY(ymid)));
0N/A */
0N/A return orderof(this.XforY(ymid), that.XforY(ymid));
0N/A }
0N/A
0N/A public static final double TMIN = 1E-3;
0N/A
0N/A public boolean findIntersect(Curve that, double yrange[], double ymin,
0N/A int slevel, int tlevel,
0N/A double s0, double xs0, double ys0,
0N/A double s1, double xs1, double ys1,
0N/A double t0, double xt0, double yt0,
0N/A double t1, double xt1, double yt1)
0N/A {
0N/A /*
0N/A String pad = " ";
0N/A pad = pad+pad+pad+pad+pad;
0N/A pad = pad+pad;
0N/A System.out.println("----------------------------------------------");
0N/A System.out.println(pad.substring(0, slevel)+ys0);
0N/A System.out.println(pad.substring(0, slevel)+ys1);
0N/A System.out.println(pad.substring(0, slevel)+(s1-s0));
0N/A System.out.println("-------");
0N/A System.out.println(pad.substring(0, tlevel)+yt0);
0N/A System.out.println(pad.substring(0, tlevel)+yt1);
0N/A System.out.println(pad.substring(0, tlevel)+(t1-t0));
0N/A */
0N/A if (ys0 > yt1 || yt0 > ys1) {
0N/A return false;
0N/A }
0N/A if (Math.min(xs0, xs1) > Math.max(xt0, xt1) ||
0N/A Math.max(xs0, xs1) < Math.min(xt0, xt1))
0N/A {
0N/A return false;
0N/A }
0N/A // Bounding boxes intersect - back off the larger of
0N/A // the two subcurves by half until they stop intersecting
0N/A // (or until they get small enough to switch to a more
0N/A // intensive algorithm).
0N/A if (s1 - s0 > TMIN) {
0N/A double s = (s0 + s1) / 2;
0N/A double xs = this.XforT(s);
0N/A double ys = this.YforT(s);
0N/A if (s == s0 || s == s1) {
0N/A System.out.println("s0 = "+s0);
0N/A System.out.println("s1 = "+s1);
0N/A throw new InternalError("no s progress!");
0N/A }
0N/A if (t1 - t0 > TMIN) {
0N/A double t = (t0 + t1) / 2;
0N/A double xt = that.XforT(t);
0N/A double yt = that.YforT(t);
0N/A if (t == t0 || t == t1) {
0N/A System.out.println("t0 = "+t0);
0N/A System.out.println("t1 = "+t1);
0N/A throw new InternalError("no t progress!");
0N/A }
0N/A if (ys >= yt0 && yt >= ys0) {
0N/A if (findIntersect(that, yrange, ymin, slevel+1, tlevel+1,
0N/A s0, xs0, ys0, s, xs, ys,
0N/A t0, xt0, yt0, t, xt, yt)) {
0N/A return true;
0N/A }
0N/A }
0N/A if (ys >= yt) {
0N/A if (findIntersect(that, yrange, ymin, slevel+1, tlevel+1,
0N/A s0, xs0, ys0, s, xs, ys,
0N/A t, xt, yt, t1, xt1, yt1)) {
0N/A return true;
0N/A }
0N/A }
0N/A if (yt >= ys) {
0N/A if (findIntersect(that, yrange, ymin, slevel+1, tlevel+1,
0N/A s, xs, ys, s1, xs1, ys1,
0N/A t0, xt0, yt0, t, xt, yt)) {
0N/A return true;
0N/A }
0N/A }
0N/A if (ys1 >= yt && yt1 >= ys) {
0N/A if (findIntersect(that, yrange, ymin, slevel+1, tlevel+1,
0N/A s, xs, ys, s1, xs1, ys1,
0N/A t, xt, yt, t1, xt1, yt1)) {
0N/A return true;
0N/A }
0N/A }
0N/A } else {
0N/A if (ys >= yt0) {
0N/A if (findIntersect(that, yrange, ymin, slevel+1, tlevel,
0N/A s0, xs0, ys0, s, xs, ys,
0N/A t0, xt0, yt0, t1, xt1, yt1)) {
0N/A return true;
0N/A }
0N/A }
0N/A if (yt1 >= ys) {
0N/A if (findIntersect(that, yrange, ymin, slevel+1, tlevel,
0N/A s, xs, ys, s1, xs1, ys1,
0N/A t0, xt0, yt0, t1, xt1, yt1)) {
0N/A return true;
0N/A }
0N/A }
0N/A }
0N/A } else if (t1 - t0 > TMIN) {
0N/A double t = (t0 + t1) / 2;
0N/A double xt = that.XforT(t);
0N/A double yt = that.YforT(t);
0N/A if (t == t0 || t == t1) {
0N/A System.out.println("t0 = "+t0);
0N/A System.out.println("t1 = "+t1);
0N/A throw new InternalError("no t progress!");
0N/A }
0N/A if (yt >= ys0) {
0N/A if (findIntersect(that, yrange, ymin, slevel, tlevel+1,
0N/A s0, xs0, ys0, s1, xs1, ys1,
0N/A t0, xt0, yt0, t, xt, yt)) {
0N/A return true;
0N/A }
0N/A }
0N/A if (ys1 >= yt) {
0N/A if (findIntersect(that, yrange, ymin, slevel, tlevel+1,
0N/A s0, xs0, ys0, s1, xs1, ys1,
0N/A t, xt, yt, t1, xt1, yt1)) {
0N/A return true;
0N/A }
0N/A }
0N/A } else {
0N/A // No more subdivisions
0N/A double xlk = xs1 - xs0;
0N/A double ylk = ys1 - ys0;
0N/A double xnm = xt1 - xt0;
0N/A double ynm = yt1 - yt0;
0N/A double xmk = xt0 - xs0;
0N/A double ymk = yt0 - ys0;
0N/A double det = xnm * ylk - ynm * xlk;
0N/A if (det != 0) {
0N/A double detinv = 1 / det;
0N/A double s = (xnm * ymk - ynm * xmk) * detinv;
0N/A double t = (xlk * ymk - ylk * xmk) * detinv;
0N/A if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
0N/A s = s0 + s * (s1 - s0);
0N/A t = t0 + t * (t1 - t0);
0N/A if (s < 0 || s > 1 || t < 0 || t > 1) {
0N/A System.out.println("Uh oh!");
0N/A }
0N/A double y = (this.YforT(s) + that.YforT(t)) / 2;
0N/A if (y <= yrange[1] && y > yrange[0]) {
0N/A yrange[1] = y;
0N/A return true;
0N/A }
0N/A }
0N/A }
0N/A //System.out.println("Testing lines!");
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A public double refineTforY(double t0, double yt0, double y0) {
0N/A double t1 = 1;
0N/A while (true) {
0N/A double th = (t0 + t1) / 2;
0N/A if (th == t0 || th == t1) {
0N/A return t1;
0N/A }
0N/A double y = YforT(th);
0N/A if (y < y0) {
0N/A t0 = th;
0N/A yt0 = y;
0N/A } else if (y > y0) {
0N/A t1 = th;
0N/A } else {
0N/A return t1;
0N/A }
0N/A }
0N/A }
0N/A
0N/A public boolean fairlyClose(double v1, double v2) {
0N/A return (Math.abs(v1 - v2) <
0N/A Math.max(Math.abs(v1), Math.abs(v2)) * 1E-10);
0N/A }
0N/A
0N/A public abstract int getSegment(double coords[]);
0N/A}