Class EdgeEnd

  1  
  2  
  3  
  4 /*
  5  * Copyright (c) 2016 Vivid Solutions.
  6  *
  7  * All rights reserved. This program and the accompanying materials
  8  * are made available under the terms of the Eclipse Public License 2.0
  9  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
 10  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
 11  * and the Eclipse Distribution License is available at
 12  *
 13  * http://www.eclipse.org/org/documents/edl-v10.php.
 14  */
 15 package org.locationtech.jts.geomgraph;
 16  
 17 import java.io.PrintStream;
 18  
 19 import org.locationtech.jts.algorithm.BoundaryNodeRule;
 20 import org.locationtech.jts.algorithm.Orientation;
 21 import org.locationtech.jts.geom.Coordinate;
 22 import org.locationtech.jts.util.Assert;
 23  
 24 /**
 25  * Models the end of an edge incident on a node.
 26  * EdgeEnds have a direction
 27  * determined by the direction of the ray from the initial
 28  * point to the next point.
 29  * EdgeEnds are comparable under the ordering
 30  * "a has a greater angle with the x-axis than b".
 31  * This ordering is used to sort EdgeEnds around a node.
 32  * @version 1.7
 33  */
 34 public class EdgeEnd
 35   implements Comparable
 36 {
 37   protected Edge edge;  // the parent edge of this edge end
 38   protected Label label;
 39  
 40   private Node node;          // the node this edge end originates at
 41   private Coordinate p0p1;  // points of initial line segment
 42   private double dxdy;      // the direction vector for this edge from its starting point
 43   private int quadrant;
 44  
 45   protected EdgeEnd(Edge edge)
 46   {
 47     this.edge = edge;
 48   }
 49   public EdgeEnd(Edge edge, Coordinate p0, Coordinate p1) {
 50     this(edge, p0, p1, null);
 51   }
 52   public EdgeEnd(Edge edge, Coordinate p0, Coordinate p1, Label label) {
 53     this(edge);
 54     init(p0, p1);
 55     this.label = label;
 56   }
 57  
 58   protected void init(Coordinate p0, Coordinate p1)
 59   {
 60     this.p0 = p0;
 61     this.p1 = p1;
 62     dx = p1.x - p0.x;
 63     dy = p1.y - p0.y;
 64     quadrant = Quadrant.quadrant(dx, dy);
 65     Assert.isTrue(! (dx == 0 && dy == 0), "EdgeEnd with identical endpoints found");
 66   }
 67  
 68   public Edge getEdge() { return edge; }
 69   public Label getLabel() { return label; }
 70   public Coordinate getCoordinate() { return p0; }
 71   public Coordinate getDirectedCoordinate() { return p1; }
 72   public int getQuadrant() { return quadrant; }
 73   public double getDx() { return dx; }
 74   public double getDy() { return dy; }
 75  
 76   public void setNode(Node node) { this.node = node; }
 77   public Node getNode() { return node; }
 78  
 79   public int compareTo(Object obj)
 80   {
 81       EdgeEnd e = (EdgeEnd) obj;
 82       return compareDirection(e);
 83   }
 84   /**
 85    * Implements the total order relation:
 86    * <p>
 87    *    a has a greater angle with the positive x-axis than b
 88    * <p>
 89    * Using the obvious algorithm of simply computing the angle is not robust,
 90    * since the angle calculation is obviously susceptible to roundoff.
 91    * A robust algorithm is:
 92    * - first compare the quadrant.  If the quadrants
 93    * are different, it it trivial to determine which vector is "greater".
 94    * - if the vectors lie in the same quadrant, the computeOrientation function
 95    * can be used to decide the relative orientation of the vectors.
 96    */
 97   public int compareDirection(EdgeEnd e)
 98   {
 99     if (dx == e.dx && dy == e.dy)
100       return 0;
101     // if the rays are in different quadrants, determining the ordering is trivial
102     if (quadrant > e.quadrant) return 1;
103     if (quadrant < e.quadrant) return -1;
104     // vectors are in the same quadrant - check relative orientation of direction vectors
105     // this is > e if it is CCW of e
106     return Orientation.index(e.p0, e.p1, p1);
107   }
108  
109   public void computeLabel(BoundaryNodeRule boundaryNodeRule)
110   {
111     // subclasses should override this if they are using labels
112   }
113   public void print(PrintStream out)
114   {
115     double angle = Math.atan2(dy, dx);
116     String className = getClass().getName();
117     int lastDotPos = className.lastIndexOf('.');
118     String name = className.substring(lastDotPos + 1);
119     out.print("  " + name + ": " + p0 + " - " + p1 + " " + quadrant + ":" + angle + "   " + label);
120   }
121   public String toString()
122   {
123     double angle = Math.atan2(dy, dx);
124     String className = getClass().getName();
125     int lastDotPos = className.lastIndexOf('.');
126     String name = className.substring(lastDotPos + 1);
127     return "  " + name + ": " + p0 + " - " + p1 + " " + quadrant + ":" + angle + "   " + label;
128   }
129 }
130