/* * The JTS Topology Suite is a collection of Java classes that * implement the fundamental operations required to validate a given * geo-spatial data set to a known topological specification. * * Copyright (C) 2001 Vivid Solutions * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.revolsys.geometry.algorithm; import com.revolsys.util.number.Doubles; /** * A non-robust version of {@link LineIntersector}. * * @version 1.7 */ public class NonRobustLineIntersector extends LineIntersector { /** * @return true if both numbers are positive or if both numbers are negative. * Returns false if both numbers are zero. */ public static boolean isSameSignAndNonZero(final double a, final double b) { if (a == 0 || b == 0) { return false; } return a < 0 && b < 0 || a > 0 && b > 0; } public NonRobustLineIntersector() { } /* * p1-p2 and p3-p4 are assumed to be collinear (although not necessarily intersecting). Returns: * DONT_INTERSECT : the two segments do not intersect COLLINEAR : the segments intersect, in the * line segment pa-pb. pa-pb is in the same direction as p1-p2 DO_INTERSECT : the inputLines * intersect in a single point only, pa */ private int computeCollinearIntersection(final double line1x1, final double line1y1, final double line1x2, final double line1y2, final double line2x1, final double line2y1, final double line2x2, final double line2y2) { final double r1 = 0; final double r2 = 1; final double r3 = rParameter(line1x1, line1y1, line1x2, line1y2, line2x1, line2y1); final double r4 = rParameter(line1x1, line1y1, line1x2, line1y2, line2x2, line2y2); double q3x; double q3y; double q4x; double q4y; double t3; double t4; // make sure p3-p4 is in same direction as p1-p2 if (r3 < r4) { q3x = line2x1; q3y = line2y1; q4x = line2x2; q4y = line2y2; t3 = r3; t4 = r4; } else { q3x = line2x2; q3y = line2y2; q4x = line2x1; q4y = line2y1; t3 = r4; t4 = r3; } if (t3 > r2 || t4 < r1) { // check for no intersection return NO_INTERSECTION; } else if (line1x1 == q4x && line1y1 == q4y) { // check for single point intersection this.pointAX = line1x1; this.pointAY = line1y1; return POINT_INTERSECTION; } else if (line1x2 == q3x && line1y2 == q3y) { this.pointAX = line1x2; this.pointAY = line1y2; return POINT_INTERSECTION; } else { // intersection MUST be a segment - compute endpoints if (t3 > r1) { this.pointAX = line2x1; this.pointAY = line2y1; } else { this.pointAX = line1x1; this.pointAY = line1y1; } if (t4 < r2) { this.pointBX = line2x2; this.pointBY = line2y2; } else { this.pointBX = line1x2; this.pointBY = line1y2; } return COLLINEAR_INTERSECTION; } } @Override protected int computeIntersect(final double line1x1, final double line1y1, final double line1x2, final double line1y2, final double line2x1, final double line2y1, final double line2x2, final double line2y2) { this.isProper = false; /* * Compute a1, b1, c1, where line joining points 1 and 2 is "a1 x + b1 y + c1 = 0". */ final double a1 = line1y2 - line1y1; final double b1 = line1x1 - line1x2; final double c1 = line1x2 * line1y1 - line1x1 * line1y2; /* * Compute r3 and r4. */ final double r3 = a1 * line2x1 + b1 * line2y1 + c1; final double r4 = a1 * line2x2 + b1 * line2y2 + c1; /* * Check signs of r3 and r4. If both point 3 and point 4 lie on same side of line 1, the line * segments do not intersect. */ if (r3 != 0 && r4 != 0 && isSameSignAndNonZero(r3, r4)) { return NO_INTERSECTION; } /* * Coefficients of line eqns. */ final double a2 = line2y2 - line2y1; final double b2 = line2x1 - line2x2; final double c2 = line2x2 * line2y1 - line2x1 * line2y2; /* * Compute r1 and r2 */ final double r1 = a2 * line1x1 + b2 * line1y1 + c2; final double r2 = a2 * line1x2 + b2 * line1y2 + c2; /* * Check signs of r1 and r2. If both point 1 and point 2 lie on same side of second line * segment, the line segments do not intersect. */ if (r1 != 0 && r2 != 0 && isSameSignAndNonZero(r1, r2)) { return NO_INTERSECTION; } /** * Line segments intersect: compute intersection point. */ final double denom = a1 * b2 - a2 * b1; if (denom == 0) { return computeCollinearIntersection(line1x1, line1y1, line1x2, line1y2, line2x1, line2y1, line2x2, line2y2); } final double numX = b1 * c2 - b2 * c1; /* * TESTING ONLY double valX = (( num < 0 ? num - offset : num + offset ) / denom); double * valXInt = (int) (( num < 0 ? num - offset : num + offset ) / denom); if (valXInt != pa.x) // * TESTING ONLY System.out.println(val + " - int: " + valInt + ", floor: " + pa.x); */ final double numY = a2 * c1 - a1 * c2; this.pointAX = numX / denom; this.pointAY = numY / denom; // check if this is a proper intersection BEFORE truncating values, // to avoid spurious equality comparisons with endpoints this.isProper = true; if (this.pointAX == line1x1 && this.pointAY == line1y1 || this.pointAX == line1x2 && this.pointAY == line1y2 || this.pointAX == line2x1 && this.pointAY == line2y1 || this.pointAX == line2x2 && this.pointAY == line2y2) { this.isProper = false; } this.pointAX = Doubles.makePrecise(this.scale, this.pointAX); this.pointAY = Doubles.makePrecise(this.scale, this.pointAY); return POINT_INTERSECTION; } @Override public boolean computeIntersection(final double x, final double y, final double x1, final double y1, final double x2, final double y2) { this.isProper = false; /* * Compute a1, b1, c1, where line joining points 1 and 2 is "a1 x + b1 y + c1 = 0". */ final double a1 = y2 - y1; final double b1 = x1 - x2; final double c1 = x2 * y1 - x1 * y2; /* * Compute r3 and r4. */ final double r = a1 * x + b1 * y + c1; // if r != 0 the point does not lie on the line if (r != 0) { this.intersectionCount = NO_INTERSECTION; return false; } // Point lies on line - check to see whether it lies in line segment. final double dist = rParameter(x, y, x1, y1, x2, y2); if (dist < 0.0 || dist > 1.0) { this.intersectionCount = NO_INTERSECTION; return false; } this.isProper = true; if (x == x1 && y == y1 || x == x2 && y == y2) { this.isProper = false; } this.intersectionCount = POINT_INTERSECTION; return true; } /** * RParameter computes the parameter for the point p * in the parameterized equation * of the line from p1 to p2. * This is equal to the 'distance' of p along p1-p2 */ private double rParameter(final double x, final double y, final double x1, final double y1, final double x2, final double y2) { final double dx = Math.abs(x2 - x1); final double dy = Math.abs(y2 - y1); // compute maximum delta, for numerical stability // also handle case of p1-p2 being vertical or horizontal double r; if (dx > dy) { r = (x - x1) / (x2 - x1); } else { r = (y - y1) / (y2 - y1); } return r; } }