/*
* 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.planargraph;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.revolsys.geometry.algorithm.CGAlgorithms;
import com.revolsys.geometry.algorithm.CGAlgorithmsDD;
import com.revolsys.geometry.geomgraph.Quadrant;
import com.revolsys.geometry.model.Point;
/**
* Represents a directed edge in a {@link PlanarGraph}. A DirectedEdge may or
* may not have a reference to a parent {@link Edge} (some applications of
* planar graphs may not require explicit Edge objects to be created). Usually
* a client using a <code>PlanarGraph</code> will subclass <code>DirectedEdge</code>
* to add its own application-specific data and methods.
*
* @version 1.7
*/
public class DirectedEdge extends GraphComponent implements Comparable<DirectedEdge> {
/**
* Returns a List containing the parent Edge (possibly null) for each of the given
* DirectedEdges.
*/
public static List<Edge> toEdges(final Collection<DirectedEdge> dirEdges) {
final List<Edge> edges = new ArrayList<>();
for (final DirectedEdge directedEdge : dirEdges) {
edges.add(directedEdge.parentEdge);
}
return edges;
}
private final double angle;
private final boolean edgeDirection;
private final Node from;
private final Point directionPoint;
private Edge parentEdge;
private final int quadrant;
private DirectedEdge sym = null; // optional
private final Node to;
/**
* Constructs a DirectedEdge connecting the <code>from</code> node to the
* <code>to</code> node.
*
* @param directionPoint
* specifies this DirectedEdge's direction vector
* (determined by the vector from the <code>from</code> node
* to <code>directionPt</code>)
* @param edgeDirection
* whether this DirectedEdge's direction is the same as or
* opposite to that of the parent Edge (if any)
*/
public DirectedEdge(final Node from, final Node to, final Point directionPoint,
final boolean edgeDirection) {
this.from = from;
this.to = to;
this.edgeDirection = edgeDirection;
this.directionPoint = directionPoint;
final double dx = this.directionPoint.getX() - from.getX();
final double dy = this.directionPoint.getY() - from.getY();
this.quadrant = Quadrant.quadrant(dx, dy);
this.angle = Math.atan2(dy, dx);
// Assert.isTrue(! (dx == 0 && dy == 0),
// "EdgeEnd with identical endpoints found");
}
/**
* Returns 1 if this DirectedEdge has a greater angle with the
* positive x-axis than b", 0 if the DirectedEdges are collinear, and -1 otherwise.
* <p>
* Using the obvious algorithm of simply computing the angle is not robust,
* since the angle calculation is susceptible to roundoff. A robust algorithm
* is:
* <ul>
* <li>first compare the quadrants. If the quadrants are different, it it
* trivial to determine which vector is "greater".
* <li>if the vectors lie in the same quadrant, the robust
* {@link CGAlgorithms#computeOrientation(Point, Point, Point)}
* function can be used to decide the relative orientation of the vectors.
* </ul>
*/
public int compareDirection(final DirectedEdge e) {
// 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.from, e.directionPoint, this.directionPoint);
// testing only
// return ShewchuksDeterminant.orientationIndex(p1, p2, q);
// previous implementation - not quite fully robust
// return RobustDeterminant.orientationIndex(p1, p2, q);
}
/**
* Returns 1 if this DirectedEdge has a greater angle with the
* positive x-axis than b", 0 if the DirectedEdges are collinear, and -1 otherwise.
* <p>
* Using the obvious algorithm of simply computing the angle is not robust,
* since the angle calculation is susceptible to roundoff. A robust algorithm
* is:
* <ul>
* <li>first compare the quadrants. If the quadrants are different, it it
* trivial to determine which vector is "greater".
* <li>if the vectors lie in the same quadrant, the robust
* {@link CGAlgorithms#computeOrientation(Point, Point, Point)}
* function can be used to decide the relative orientation of the vectors.
* </ul>
*/
@Override
public int compareTo(final DirectedEdge de) {
return compareDirection(de);
}
/**
* Returns the angle that the start of this DirectedEdge makes with the
* positive x-axis, in radians.
*/
public double getAngle() {
return this.angle;
}
/**
* Returns a point to which an imaginary line is drawn from the from-node to
* specify this DirectedEdge's orientation.
*/
public Point getDirectionPoint() {
return this.directionPoint;
}
/**
* Returns this DirectedEdge's parent Edge, or null if it has none.
*/
public Edge getEdge() {
return this.parentEdge;
}
/**
* Returns whether the direction of the parent Edge (if any) is the same as that
* of this Directed Edge.
*/
public boolean getEdgeDirection() {
return this.edgeDirection;
}
/**
* Returns the node from which this DirectedEdge leaves.
*/
public Node getFromNode() {
return this.from;
}
/**
* Returns 0, 1, 2, or 3, indicating the quadrant in which this DirectedEdge's
* orientation lies.
*/
public int getQuadrant() {
return this.quadrant;
}
/**
* Returns the symmetric DirectedEdge -- the other DirectedEdge associated with
* this DirectedEdge's parent Edge.
*/
public DirectedEdge getSym() {
return this.sym;
}
/**
* Returns the node to which this DirectedEdge goes.
*/
public Node getToNode() {
return this.to;
}
/**
* Tests whether this directed edge has been removed from its containing graph
*
* @return <code>true</code> if this directed edge is removed
*/
@Override
public boolean isRemoved() {
return this.parentEdge == null;
}
/**
* Prints a detailed string representation of this DirectedEdge to the given PrintStream.
*/
public void print(final PrintStream out) {
final String className = getClass().getName();
final int lastDotPos = className.lastIndexOf('.');
final String name = className.substring(lastDotPos + 1);
out.print(" " + name + ": " + this.from + " - " + this.directionPoint + " " + this.quadrant
+ ":" + this.angle);
}
/**
* Removes this directed edge from its containing graph.
*/
void remove() {
this.sym = null;
this.parentEdge = null;
}
/**
* Associates this DirectedEdge with an Edge (possibly null, indicating no associated
* Edge).
*/
public void setEdge(final Edge parentEdge) {
this.parentEdge = parentEdge;
}
/**
* Sets this DirectedEdge's symmetric DirectedEdge, which runs in the opposite
* direction.
*/
public void setSym(final DirectedEdge sym) {
this.sym = sym;
}
}