/*
* 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.geomgraph;
import java.io.PrintStream;
import com.revolsys.geometry.algorithm.BoundaryNodeRule;
import com.revolsys.geometry.algorithm.CGAlgorithmsDD;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.util.Assert;
/**
* Models the end of an edge incident on a node.
* EdgeEnds have a direction
* determined by the direction of the ray from the initial
* point to the next point.
* EdgeEnds are comparable under the ordering
* "a has a greater angle with the x-axis than b".
* This ordering is used to sort EdgeEnds around a node.
* @version 1.7
*/
public class EdgeEnd implements Comparable<Object> {
private double dx, dy; // the direction vector for this edge from its starting
protected Edge edge; // the parent edge of this edge end
private Label label;
private Node node; // the node this edge end originates at
private Point p0, p1; // points of initial line segment
// point
private int quadrant;
protected EdgeEnd(final Edge edge) {
this.edge = edge;
}
public EdgeEnd(final Edge edge, final Point p0, final Point p1) {
this(edge, p0, p1, null);
}
public EdgeEnd(final Edge edge, final Point p0, final Point p1, final Label label) {
this(edge);
init(p0, p1);
this.setLabel(label);
}
/**
* Implements the total order relation:
* <p>
* a has a greater angle with the positive x-axis than b
* <p>
* Using the obvious algorithm of simply computing the angle is not robust,
* since the angle calculation is obviously susceptible to roundoff.
* A robust algorithm is:
* - first compare the quadrant. If the quadrants
* are different, it it trivial to determine which vector is "greater".
* - if the vectors lie in the same quadrant, the computeOrientation function
* can be used to decide the relative orientation of the vectors.
*/
public int compareDirection(final EdgeEnd e) {
final double dx = getDx();
final double dy = getDy();
if (dx == e.getDx() && dy == e.getDy()) {
return 0;
}
// if the rays are in different quadrants, determining the ordering is
// trivial
if (this.quadrant > e.quadrant) {
return 1;
}
if (this.quadrant < e.quadrant) {
return -1;
}
// vectors are in the same quadrant - check relative orientation of
// direction vectors
// this is > e if it is CCW of e
/**
* MD - 9 Aug 2010 It seems that the basic algorithm is slightly orientation
* dependent, when computing the orientation of a point very close to a
* line. This is possibly due to the arithmetic in the translation to the
* origin.
*
* For instance, the following situation produces identical results in spite
* of the inverse orientation of the line segment:
*
* Point p0 = new PointDouble((double)219.3649559090992, 140.84159161824724);
* Point p1 = new PointDouble((double)168.9018919682399, -5.713787599646864);
*
* Point p = new PointDouble((double)186.80814046338352, 46.28973405831556); int
* orient = orientationIndex(p0, p1, p); int orientInv =
* orientationIndex(p1, p0, p);
*
* A way to force consistent results is to normalize the orientation of the
* vector using the following code. However, this may make the results of
* orientationIndex inconsistent through the triangle of points, so it's not
* clear this is an appropriate patch.
*
*/
return CGAlgorithmsDD.orientationIndex(e.p0, e.p1, this.p1);
// testing only
// return ShewchuksDeterminant.orientationIndex(p1, p2, q);
// previous implementation - not quite fully robust
// return RobustDeterminant.orientationIndex(p1, p2, q);
}
@Override
public int compareTo(final Object obj) {
final EdgeEnd e = (EdgeEnd)obj;
return compareDirection(e);
}
public void computeLabel(final BoundaryNodeRule boundaryNodeRule) {
// subclasses should override this if they are using labels
}
public Point getCoordinate() {
return this.p0;
}
public Point getDirectedCoordinate() {
return this.p1;
}
public double getDx() {
return this.dx;
}
public double getDy() {
return this.dy;
}
public Edge getEdge() {
return this.edge;
}
public Label getLabel() {
return this.label;
}
public Node getNode() {
return this.node;
}
public int getQuadrant() {
return this.quadrant;
}
protected void init(final Point p0, final Point p1) {
this.p0 = p0;
this.p1 = p1;
this.dx = p1.getX() - p0.getX();
this.dy = p1.getY() - p0.getY();
this.quadrant = Quadrant.quadrant(this.dx, this.dy);
Assert.isTrue(!(this.dx == 0 && this.dy == 0), "EdgeEnd with identical endpoints found");
}
public void print(final PrintStream out) {
final double angle = Math.atan2(getDy(), getDx());
final String className = getClass().getName();
final int lastDotPos = className.lastIndexOf('.');
final String name = className.substring(lastDotPos + 1);
out.print(" " + name + ": " + this.p0 + " - " + this.p1 + " " + this.quadrant + ":" + angle
+ " " + getLabel());
}
protected void setLabel(final Label label) {
this.label = label;
}
public void setNode(final Node node) {
this.node = node;
}
@Override
public String toString() {
final double angle = Math.atan2(getDy(), getDx());
final String className = getClass().getName();
final int lastDotPos = className.lastIndexOf('.');
final String name = className.substring(lastDotPos + 1);
return " " + name + ": " + this.p0 + " - " + this.p1 + " " + this.quadrant + ":" + angle
+ " " + getLabel();
}
}