All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.shaded.org.locationtech.jts.edgegraph.HalfEdge Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2016 Vivid Solutions.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
 * and the Eclipse Distribution License is available at
 *
 * http://www.eclipse.org/org/documents/edl-v10.php.
 */

package com.hazelcast.shaded.org.locationtech.jts.edgegraph;

import com.hazelcast.shaded.org.locationtech.jts.algorithm.Orientation;
import com.hazelcast.shaded.org.locationtech.jts.geom.Coordinate;
import com.hazelcast.shaded.org.locationtech.jts.geom.Quadrant;
import com.hazelcast.shaded.org.locationtech.jts.io.WKTWriter;
import com.hazelcast.shaded.org.locationtech.jts.util.Assert;

/**
 * Represents a directed component of an edge in an {@link EdgeGraph}.
 * HalfEdges link vertices whose locations are defined by {@link Coordinate}s.
 * HalfEdges start at an origin vertex,
 * and terminate at a destination vertex.
 * HalfEdges always occur in symmetric pairs, with the {@link #sym()} method
 * giving access to the oppositely-oriented component.
 * HalfEdges and the methods on them form an edge algebra,
 * which can be used to traverse and query the topology
 * of the graph formed by the edges.
 * 

* To support graphs where the edges are sequences of coordinates * each edge may also have a direction point supplied. * This is used to determine the ordering * of the edges around the origin. * HalfEdges with the same origin are ordered * so that the ring of edges formed by them is oriented CCW. *

* By design HalfEdges carry minimal information * about the actual usage of the graph they represent. * They can be subclassed to carry more information if required. *

* HalfEdges form a complete and consistent data structure by themselves, * but an {@link EdgeGraph} is useful to allow retrieving edges * by vertex and edge location, as well as ensuring * edges are created and linked appropriately. * * @author Martin Davis * */ public class HalfEdge { /** * Creates a HalfEdge pair representing an edge * between two vertices located at coordinates p0 and p1. * * @param p0 a vertex coordinate * @param p1 a vertex coordinate * @return the HalfEdge with origin at p0 */ public static HalfEdge create(Coordinate p0, Coordinate p1) { HalfEdge e0 = new HalfEdge(p0); HalfEdge e1 = new HalfEdge(p1); e0.link(e1); return e0; } private Coordinate orig; private HalfEdge sym; private HalfEdge next; /** * Creates a half-edge originating from a given coordinate. * * @param orig the origin coordinate */ public HalfEdge(Coordinate orig) { this.orig = orig; } /** * Links this edge with its sym (opposite) edge. * This must be done for each pair of edges created. * * @param sym the sym edge to link. */ public void link(HalfEdge sym) { setSym(sym); sym.setSym(this); // set next ptrs for a single segment setNext(sym); sym.setNext(this); } /** * Gets the origin coordinate of this edge. * * @return the origin coordinate */ public Coordinate orig() { return orig; } /** * Gets the destination coordinate of this edge. * * @return the destination coordinate */ public Coordinate dest() { return sym.orig; } /** * The X component of the direction vector. * * @return the X component of the direction vector */ double directionX() { return directionPt().getX() - orig.getX(); } /** * The Y component of the direction vector. * * @return the Y component of the direction vector */ double directionY() { return directionPt().getY() - orig.getY(); } /** * Gets the direction point of this edge. * In the base case this is the dest coordinate * of the edge. * Subclasses may override to * allow a HalfEdge to represent an edge with more than two coordinates. * * @return the direction point for the edge */ protected Coordinate directionPt() { // default is to assume edges have only 2 vertices // subclasses may override to provide an internal direction point return dest(); } /** * Gets the symmetric pair edge of this edge. * * @return the symmetric pair edge */ public HalfEdge sym() { return sym; } /** * Sets the symmetric (opposite) edge to this edge. * * @param e the sym edge to set */ private void setSym(HalfEdge e) { sym = e; } /** * Sets the next edge CCW around the destination vertex of this edge. * * @param e the next edge */ private void setNext(HalfEdge e) { next = e; } /** * Gets the next edge CCW around the * destination vertex of this edge, * originating at that vertex. * If the destination vertex has degree 1 then this is the sym edge. * * @return the next outgoing edge CCW around the destination vertex */ public HalfEdge next() { return next; } /** * Gets the previous edge CW around the origin * vertex of this edge, * with that vertex being its destination. *

* It is always true that e.next().prev() == e *

* Note that this requires a scan of the origin edges, * so may not be efficient for some uses. * * @return the previous edge CW around the origin vertex */ public HalfEdge prev() { HalfEdge curr = this; HalfEdge prev = this; do { prev = curr; curr = curr.oNext(); } while (curr != this); return prev.sym; } /** * Gets the next edge CCW around the origin of this edge, * with the same origin. * If the origin vertex has degree 1 then this is the edge itself. *

* e.oNext() is equal to e.sym().next() * * @return the next edge around the origin */ public HalfEdge oNext() { return sym.next; } /** * Finds the edge starting at the origin of this edge * with the given dest vertex, * if any. * * @param dest the dest vertex to search for * @return the edge with the required dest vertex, if it exists, * or null */ public HalfEdge find(Coordinate dest) { HalfEdge oNext = this; do { if (oNext == null) return null; if (oNext.dest().equals2D(dest)) return oNext; oNext = oNext.oNext(); } while (oNext != this); return null; } /** * Tests whether this edge has the given orig and dest vertices. * * @param p0 the origin vertex to test * @param p1 the destination vertex to test * @return true if the vertices are equal to the ones of this edge */ public boolean equals(Coordinate p0, Coordinate p1) { return orig.equals2D(p0) && sym.orig.equals(p1); } /** * Inserts an edge * into the ring of edges around the origin vertex of this edge, * ensuring that the edges remain ordered CCW. * The inserted edge must have the same origin as this edge. * * @param eAdd the edge to insert */ public void insert(HalfEdge eAdd) { // If this is only edge at origin, insert it after this if (oNext() == this) { // set linkage so ring is correct insertAfter(eAdd); return; } // Scan edges until insertion point is found HalfEdge ePrev = insertionEdge(eAdd); ePrev.insertAfter(eAdd); } /** * Finds the insertion edge for a edge * being added to this origin, * ensuring that the star of edges * around the origin remains fully CCW. * * @param eAdd the edge being added * @return the edge to insert after */ private HalfEdge insertionEdge(HalfEdge eAdd) { HalfEdge ePrev = this; do { HalfEdge eNext = ePrev.oNext(); /** * Case 1: General case, * with eNext higher than ePrev. * * Insert edge here if it lies between ePrev and eNext. */ if (eNext.compareTo(ePrev) > 0 && eAdd.compareTo(ePrev) >= 0 && eAdd.compareTo(eNext) <= 0) { return ePrev; } /** * Case 2: Origin-crossing case, * indicated by eNext <= ePrev. * * Insert edge here if it lies * in the gap between ePrev and eNext across the origin. */ if (eNext.compareTo(ePrev) <= 0 && (eAdd.compareTo(eNext) <= 0 || eAdd.compareTo(ePrev) >= 0)) { return ePrev; } ePrev = eNext; } while (ePrev != this); Assert.shouldNeverReachHere(); return null; } /** * Insert an edge with the same origin after this one. * Assumes that the inserted edge is in the correct * position around the ring. * * @param e the edge to insert (with same origin) */ private void insertAfter(HalfEdge e) { Assert.equals(orig, e.orig()); HalfEdge save = oNext(); sym.setNext(e); e.sym().setNext(save); } /** * Tests whether the edges around the origin * are sorted correctly. * Note that edges must be strictly increasing, * which implies no two edges can have the same direction point. * * @return true if the origin edges are sorted correctly */ public boolean isEdgesSorted() { // find lowest edge at origin HalfEdge lowest = findLowest(); HalfEdge e = lowest; // check that all edges are sorted do { HalfEdge eNext = e.oNext(); if (eNext == lowest) break; boolean isSorted = eNext.compareTo(e) > 0; if (! isSorted) { //int comp = eNext.compareTo(e); return false; } e = eNext; } while (e != lowest); return true; } /** * Finds the lowest edge around the origin, * using the standard edge ordering. * * @return the lowest edge around the origin */ private HalfEdge findLowest() { HalfEdge lowest = this; HalfEdge e = this.oNext(); do { if (e.compareTo(lowest) < 0) lowest = e; e = e.oNext(); } while (e != this); return lowest; } /** * Compares edges which originate at the same vertex * based on the angle they make at their origin vertex with the positive X-axis. * This allows sorting edges around their origin vertex in CCW order. */ public int compareTo(Object obj) { HalfEdge e = (HalfEdge) obj; int comp = compareAngularDirection(e); return comp; } /** * Implements the total order relation: *

* The angle of edge a is greater than the angle of edge b, * where the angle of an edge is the angle made by * the first segment of the edge with the positive x-axis *

* When applied to a list of edges originating at the same point, * this produces a CCW ordering of the edges around the point. *

* Using the obvious algorithm of computing the angle is not robust, * since the angle calculation is susceptible to roundoff error. * A robust algorithm is: *

    *
  • First, compare the quadrants the edge vectors lie in. * If the quadrants are different, * it is trivial to determine which edge has a greater angle. * *
  • if the vectors lie in the same quadrant, the * {@link Orientation#index(Coordinate, Coordinate, Coordinate)} function * can be used to determine the relative orientation of the vectors. *
*/ public int compareAngularDirection(HalfEdge e) { double dx = directionX(); double dy = directionY(); double dx2 = e.directionX(); double dy2 = e.directionY(); // same vector if (dx == dx2 && dy == dy2) return 0; int quadrant = Quadrant.quadrant(dx, dy); int quadrant2 = Quadrant.quadrant(dx2, dy2); /** * If the direction vectors are in different quadrants, * that determines the ordering */ if (quadrant > quadrant2) return 1; if (quadrant < quadrant2) return -1; //--- vectors are in the same quadrant // Check relative orientation of direction vectors // this is > e if it is CCW of e Coordinate dir1 = directionPt(); Coordinate dir2 = e.directionPt(); return Orientation.index(e.orig, dir2, dir1); } /** * Provides a string representation of a HalfEdge. * * @return a string representation */ public String toString() { return "HE("+orig.x + " " + orig.y + ", " + sym.orig.x + " " + sym.orig.y + ")"; } /** * Provides a string representation of the edges around * the origin node of this edge. * Uses the subclass representation for each edge. * * @return a string showing the edges around the origin */ public String toStringNode() { Coordinate orig = orig(); Coordinate dest = dest(); StringBuilder sb = new StringBuilder(); sb.append("Node( " + WKTWriter.format(orig) + " )" + "\n"); HalfEdge e = this; do { sb.append(" -> " + e); sb.append("\n"); e = e.oNext(); } while (e != this); return sb.toString(); } /** * Computes the degree of the origin vertex. * The degree is the number of edges * originating from the vertex. * * @return the degree of the origin vertex */ public int degree() { int degree = 0; HalfEdge e = this; do { degree++; e = e.oNext(); } while (e != this); return degree; } /** * Finds the first node previous to this edge, if any. * A node has degree {@code <> 2}. * If no such node exists (i.e. the edge is part of a ring) * then null is returned. * * @return an edge originating at the node prior to this edge, if any, * or null if no node exists */ public HalfEdge prevNode() { HalfEdge e = this; while (e.degree() == 2) { e = e.prev(); if (e == this) return null; } return e; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy