/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2001-2006 Vivid Solutions
* (C) 2001-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotools.geometry.iso.util.algorithm2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
/**
* @author roehrig
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*
* @source $URL$
*/
public class LineLineIntersection2D {
// the range of the returned values is from 0.0 to 1.0
// Overlaping:
// (p0,p1) *------------->* *------------->* *------->*
// (q0,q1) *------------->* *------>* *------------->*
// result: (0.0,1.0,0.0,1.0) (0.2,0.8,NaN,NaN) (NaN,NaN,0.2,0.8)
//
// (p0,p1) *------------->* *------------->*
// (q0,q1) *------------->* *------------->*
// result: (0.2,NaN,NaN,0.8) (NaN,0,8,0.2,NaN)
//
// (p0,p1) *--------->* *------------->* *------->* *------------->*
// (q0,q1) *------------->* *--------->* *------------->* *-------->*
// result: (0.0,NaN,0.0,0.8) (0.0,0.8,0.0,NaN) (NaN,1.0,0.4,1.0) (0.3,1.0,Nan,1.0)
//
// (p0,p1) *----->* *------->* *----->* *------->*
// (q0,q1) *------>* *----->* *------>* *----->*
// result: (1.0,NaN,NaN,0.0) (NaN,0.0,1.0,NaN) (1.0,NaN,NaN,0.0) (NaN,0.0,1.0,NaN)
//
// Seg 0: \ /
// \/ result: (0.5,0.5)
// /\
// Seg1 / \
//
// |p1
// | q0_________q1 result: (0.5,NaN)
// |
// |p0
//
// q0 _________q1
//
// |p1
// | result: (NaN,0.5)
// |
// |p0
//
/**
* this class interprets the intersection method
*/
private double itsc[];
Line2D line0;
Line2D line1;
public LineLineIntersection2D() {
itsc = null;
line0=null;
line1=null;
}
public LineLineIntersection2D(Line2D line0, Line2D line1) {
setValues(line0,line1);
}
public void setValues(Line2D line0, Line2D line1) {
Point2D p0 = line0.getP1();
Point2D p1 = line0.getP2();
Point2D q0 = line1.getP1();
Point2D q1 = line1.getP2();
this.line0 = line0;
this.line1 = line1;
this.itsc = intersection(p0,p1,q0,q1);
if (itsc == null)
return;
int n = itsc.length;
if (n == 4) {
/**
* the segments are colinear and their interiors may overlap. The
* segments are equal if the four itscs are real numbers, and
* in this case the four itscs must be 0.0 or 1.0. The second
* possibility are two real numbers and two Double.NaN. In this
* case, the interiors of the segments intersect
*/
int count = 0;
if (isNaN(itsc[0]))
count++;
if (isNaN(itsc[1]))
count++;
if (isNaN(itsc[2]))
count++;
if (isNaN(itsc[3]))
count++;
if (count==3)
throw new IllegalArgumentException(
"Error on LineLineIntersection: wrong overlaping itscs. Count="+count);
if (count == 0) {
/** all real numbers, hence they coincide and must be 0.0 or 1.0 */
if ((itsc[0] != 0.0 && itsc[0] != 1.0)
|| (itsc[1] != 0.0 && itsc[1] != 1.0)
|| (itsc[2] != 0.0 && itsc[2] != 1.0)
|| (itsc[3] != 0.0 && itsc[3] != 1.0)) {
throw new IllegalArgumentException(
"Error on LineLineIntersection: wrong coincident itscs");
}
} else if (count == 2) {
}
} else if (n == 2) {
}
}
private boolean isNaN(double d) {
return java.lang.Double.isNaN(d);
}
public boolean isCoincident() {
/**
* the segments are coincident if they overlap and the intersection
* itscs are 0.0 or 1.0. This implies no NaN in the itscs
*/
return (itsc != null) && (itsc.length == 4)
&& !isNaN(itsc[0]) && !isNaN(itsc[1])
&& !isNaN(itsc[2]) && !isNaN(itsc[3]);
}
public boolean overlapsInterior() {
/**
* the interiors of the segments overlap if at least one of the four
* itsc is less then 1.0 and greater then 0.0
*/
return ((itsc != null) && (itsc.length == 4)
&& ( (!isNaN(itsc[0]) && itsc[0] > 0.0 && itsc[0] < 1.0) ||
(!isNaN(itsc[1]) && itsc[1] > 0.0 && itsc[1] < 1.0) ||
(!isNaN(itsc[1]) && itsc[2] > 0.0 && itsc[2] < 1.0) ||
(!isNaN(itsc[1]) && itsc[3] > 0.0 && itsc[3] < 1.0) ));
}
public boolean intersectInteriorInteriorPoint() {
/**
* the interiors of the segments intersect at a node if both
* itscs are real number not equal 0.0 and not equal 1.0
*/
if ((itsc == null) || (itsc.length != 2))
return false;
return ((!isNaN(itsc[0]) && itsc[0] != 0.0 && itsc[0] != 1.0) &&
(!isNaN(itsc[1]) && itsc[1] != 0.0 && itsc[1] != 1.0));
}
public boolean isII() {
/**
* the interiors of the segments intersect if they intersect at one node or
* if their interior overlap number and two itscs are Double.NaN
*/
return intersectInteriorInteriorPoint() || overlapsInterior();
}
public boolean isIB() {
/**
* the interior of the line0 intersects the boundary of line1 if they
* have two itscs and the first itsc is a real number between 0.0 and
* 1.0 and the second itsc is a real number equal 0.0 or 1.0
*
* (itsc[0]>0.0 and itsc[0]<1.0) and (itsc[1]==0.0 or itsc[1]==1.0)
*
*/
return ((itsc != null) && (itsc.length == 2) && !isNaN(itsc[0]) && !isNaN(itsc[1])
&& (itsc[0] > 0.0 && itsc[0] < 1.0)
&& (itsc[1] == 0.0 || itsc[1] == 1.0));
}
public boolean isBI() {
/**
* the boundary of the line0 intersects the interior of line1 if they
* have two itscs and the first itsc is a real number between equal 0.0 or
* 1.0 and the second itsc is a real number between 0.0 and 1.0
*
* (itsc[0]==0.0 and itsc[0]==1.0) and (itsc[1]>0.0 or itsc[1]<1.0)
*
*/
return ((itsc != null) && (itsc.length == 2) && !isNaN(itsc[0]) && !isNaN(itsc[1])
&& (itsc[0] == 0.0 || itsc[0] == 1.0)
&& (itsc[1] > 0.0 && itsc[1] < 1.0));
}
public boolean isBB() {
/**
* the boundary of both lines intersect if they
* have two or four itscs and the first itsc is a real number
* equal 0.0 or 1.0 and the second itsc is a real itsc not equal
* 0.0 or 1.0
*
* itsc[0]==0.0 or itsc[0]==1.0
* itsc[1]!=0.0 or itsc[1]!=1.0
*
*/
if (itsc == null)
return false;
if (itsc.length == 2) {
return ((itsc[0] == 0.0 || itsc[0] == 1.0) &&
(itsc[1] == 0.0 || itsc[1] == 1.0));
} else if (itsc.length == 4) {
return isCoincident();
} else {
throw new IllegalArgumentException(
"Error on LineLineIntersection::IsBB()");
}
}
public Point2D[] intersectionPoint() {
/**
* returns one intersection node for a 0-d intersection or
* two intersection points for a 1-d intersection (overlaping)
*/
Point2D[] result = null;
if (itsc==null) return null;
else if ( itsc.length == 2 ) {
if ( !isNaN(itsc[0]) ) {
result = new Point2D[1];
result[0] = AlgoLine2D.evaluate(line0,itsc[0]);
} else if ( !isNaN(itsc[1]) ) {
result = new Point2D[1];
result[0] = AlgoLine2D.evaluate(line0,itsc[1]);
} else {
result = null;
}
} else if ( itsc.length == 4 ) {
int count = 0;
int index[] = new int[4];
if (!isNaN(itsc[0])) index[count++] = 0;
if (!isNaN(itsc[1])) index[count++] = 1;
if (!isNaN(itsc[2])) index[count++] = 2;
if (!isNaN(itsc[3])) index[count++] = 3;
Line2D l0 = (index[0]<2) ? line0 : line1;
Line2D l1 = (index[1]<2) ? line0 : line1;
result = new Point2D[2];
if (count==0) {
result = null;
} else if (count==2) {
if ((itsc[index[0]]==0.0 || itsc[index[0]]==1.0) &&
(itsc[index[1]]==0.0 || itsc[index[1]]==1.0)) {
/** colinear touching the boundaries */
result = new Point2D[1];
result[0] = AlgoLine2D.evaluate(l0,itsc[index[0]]);
} else {
/** colinear overlaping interiors */
result = new Point2D[2];
result[0] = AlgoLine2D.evaluate(l0,itsc[index[0]]);
result[1] = AlgoLine2D.evaluate(l1,itsc[index[1]]);
}
} else if (count==3) {
/** colinear overlaping and intersecting the boundary at one node */
// (p0,p1) *--------->* *------------->* *------->* *------------->*
// (q0,q1) *------------->* *--------->* *------------->* *-------->*
// result: (0.0,NaN,0.0,0.8) (0.0,0.8,0.0,NaN) (NaN,1.0,0.4,1.0) (0.3,1.0,Nan,1.0)
if (index[0]<2 && index[1]<2) {
result[0] = AlgoLine2D.evaluate(l0,itsc[index[0]]);
result[1] = AlgoLine2D.evaluate(l0,itsc[index[1]]);
} else {
result[0] = AlgoLine2D.evaluate(l1,itsc[index[0]]);
result[1] = AlgoLine2D.evaluate(l1,itsc[index[1]]);
}
} else if (count==4) {
result[0] = AlgoLine2D.evaluate(l0,itsc[index[0]]);
result[1] = AlgoLine2D.evaluate(l0,itsc[index[1]]);
}
}
return result;
}
// public Point2D[] intersectionPoint1() {
// if ( itsc.length == 2 ) {
// if ( !isNaN(itsc[0]) ) itsc[0] = itsc[0] * AlgoLine.length(line0);
// if ( !isNaN(itsc[1]) ) itsc[1] = itsc[1] * AlgoLine.length(line1);
// } else if ( itsc.length == 4 ) {
// if ( !java.lang.Double.isNaN(itsc[0]) ) itsc[0] = itsc[0] * AlgoLine.length(line0);
// if ( !java.lang.Double.isNaN(itsc[1]) ) itsc[1] = itsc[1] * AlgoLine.length(line0);
// if ( !java.lang.Double.isNaN(itsc[2]) ) itsc[2] = itsc[2] * AlgoLine.length(line1);
// if ( !java.lang.Double.isNaN(itsc[3]) ) itsc[3] = itsc[3] * AlgoLine.length(line1);
// }
// return null;
// }
// This method returns:
// 1) four parameters if the segments are overlaped, the first two parameters
// corresponding to the intersection points on this segment and the last
// two on the other segment. If both first parameters are Double.NaN,
// this segment contains the other segment. If both last parameters are
// Double.NaN, this segment is within the other one.
// 2) two parameters if the interior or the boundary of this segment intersects
// the interior or the boundary of the other segment. If the boundaries
// intersect, the parameters are rounded to exactly startParam or endParam
public static double[] intersection(Point2D p0, Point2D p1, Point2D q0, Point2D q1){
// the range of the returned values is from 0.0 to 1.0
// Overlaping:
// (p0,p1) *------------->* *------------->* *------->* *------------->* *------------->*
// (q0,q1) *------------->* *------>* *------------->* *------------->* *------------->*
// result: (0.0,1.0,0.0,1.0) (0.2,0.8,NaN,NaN) (NaN,NaN,0.2,0.8) (0.2,NaN,NaN,0.8) (NaN,0,8,0.2,NaN)
//
// (p0,p1) *----->* *------->* *----->* *------->*
// (q0,q1) *------>* *----->* *------>* *----->*
// result: (1.0,NaN,NaN,0.0) (NaN,0.0,1.0,NaN) (1.0,NaN,NaN,0.0) (NaN,0.0,1.0,NaN)
// Seg 0: \ /
// \/ result: (0.5,0.5)
// /\
// Seg1 / \
//
// |p1
// | q0_________q1 result: (0.5,NaN)
// |
// |p0
//
// q0 _________q1
// |p1
// | result: (NaN,0.5)
// |
// |p0
//
if ( AlgoLine2D.isParallel(p0, p1, q0, q1) ) {// the segments are
// parallel
double[] result = new double[4];
result[0] = java.lang.Double.NaN;
result[1] = java.lang.Double.NaN;
result[2] = java.lang.Double.NaN;
result[3] = java.lang.Double.NaN;
// return nothing if they are not on the same line or outside the line
result[0] = AlgoLine2D.constrParamForPoint(p0, p1, q0);
result[1] = AlgoLine2D.constrParamForPoint(p0, p1, q1);
result[2] = AlgoLine2D.constrParamForPoint(q0, q1, p0);
result[3] = AlgoLine2D.constrParamForPoint(q0, q1, p1);
for ( int i = 0 ; i <= 3; ++i) {
if ( result[i] < 0.0 || result[i] > 1.0 ) result[i] = java.lang.Double.NaN;
}
return result;
} else {
//Dim result[1] As double
//result = intersectionConstrParam(p0, p1, q0, q1)
// return nothing if the the four points are on the same line (det <= eps)
double[] result = new double[2];
result[0] = java.lang.Double.NaN;
result[1] = java.lang.Double.NaN;
Point2D ep = AlgoPoint2D.subtract(p1,p0);
Point2D eq = AlgoPoint2D.subtract(q1,q0);
Point2D pq = AlgoPoint2D.subtract(q0,p0);
double det = AlgoPoint2D.cross(ep,eq); // not 0, because AlgoLine.isParallel() = false
double rp = AlgoPoint2D.cross(pq,eq) / det;
double rq = AlgoPoint2D.cross(pq,ep) / det;
double eps = AlgoPoint2D.EPSILONSQ;
if ( Math.abs(rp) < eps ) rp = 0.0;
if ( Math.abs(rp - 1.0) < eps ) rp = 1.0;
if ( Math.abs(rq) < eps ) rq = 0.0;
if ( Math.abs(rq - 1.0) < eps ) rq = 1.0;
if ( rp < 0.0 || rp > 1.0 ) rp = java.lang.Double.NaN;
if ( rq < 0.0 || rq > 1.0 ) rq = java.lang.Double.NaN;
result[0] = rp;
result[1] = rq;
return result;
}
}
}