/********************************************************************** * * GEOS - Geometry Engine Open Source * http://geos.osgeo.org * * Copyright (C) 2020 Paul Ramsey * * This is free software; you can redistribute and/or modify it under * the terms of the GNU Lesser General Public Licence as published * by the Free Software Foundation. * See the COPYING file for more information. * **********************************************************************/ #pragma once #include #include #include #include namespace geos { namespace edgegraph { // geos.edgegraph /** * Represents a directed component of an edge in an {@link EdgeGraph}. * HalfEdges link vertices whose locations are defined by {@link geom::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 * */ class GEOS_DLL HalfEdge { private: /* members */ geom::CoordinateXYZM m_orig; HalfEdge* m_sym; HalfEdge* m_next; /** * Sets the symmetric (opposite) edge to this edge. * * @param e the sym edge to set */ void setSym(HalfEdge* e) { m_sym = e; }; /** * 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 */ HalfEdge* insertionEdge(HalfEdge* eAdd); /** * 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) */ void insertAfter(HalfEdge* e); /** * Finds the lowest edge around the origin, * using the standard edge ordering. * * @return the lowest edge around the origin */ const HalfEdge* findLowest() const; protected: /** * 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 */ virtual const geom::CoordinateXYZM& directionPt() const { return dest(); }; public: /** * Creates a half-edge originating from a given coordinate. * * @param p_orig the origin coordinate */ HalfEdge(const geom::CoordinateXYZM& p_orig) : m_orig(p_orig) {}; virtual ~HalfEdge() {}; /** * Creates a HalfEge pair, using the HalfEdge type of the graph subclass. * * @param p0 a vertex coordinate * @param p1 a vertex coordinate * @return the HalfEdge with origin at p0 */ static HalfEdge* create(const geom::CoordinateXYZM& p0, const geom::CoordinateXYZM& p1); /** * Links this edge with its sym (opposite) edge. * This must be done for each pair of edges created. * * @param p_sym the sym edge to link. */ void link(HalfEdge* p_sym); /** * Gets the origin coordinate of this edge. * * @return the origin coordinate */ const geom::CoordinateXYZM& orig() const { return m_orig; }; /** * Gets the destination coordinate of this edge. * * @return the destination coordinate */ const geom::CoordinateXYZM& dest() const { return m_sym->m_orig; } /** * The X component of the direction vector. * * @return the X component of the direction vector */ double directionX() const { return directionPt().x - m_orig.x; } /** * The Y component of the direction vector. * * @return the Y component of the direction vector */ double directionY() const { return directionPt().y - m_orig.y; } /** * Gets the symmetric pair edge of this edge. * * @return the symmetric pair edge */ HalfEdge* sym() const { return m_sym; }; /** * Gets the next edge CCW around the * destination vertex of this edge, * with the dest vertex as its origin. * If the vertex has degree 1 then this is the sym edge. * * @return the next edge */ HalfEdge* next() const { return m_next; }; /** * Gets the edge previous to this one * (with dest being the same as this orig). * * 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 to this one */ HalfEdge* prev() const; /** * Gets the next edge CCW around the origin of this edge, * with the same origin. * * e.oNext() is equal to e.sym().next() * * @return the next edge around the origin */ HalfEdge* oNext() const { return m_sym->m_next; }; /** * Sets the next edge CCW around the destination vertex of this edge. * * @param e the next edge */ void setNext(HalfEdge* e) { m_next = e; }; /** * 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 */ HalfEdge* find(const geom::CoordinateXY& dest); /** * 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 */ bool equals(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) const; /** * 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 */ void insert(HalfEdge* eAdd); /** * 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 */ bool isEdgesSorted() const; /** * 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 * geom::Orientation::index() function * can be used to determine the relative orientation of the vectors. */ int compareAngularDirection(const HalfEdge* e) const; int compareTo(const HalfEdge* e) const { return compareAngularDirection(e); }; /** * 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 */ int degree(); /** * Finds the first node previous to this edge, if any. * A node has degree <> 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 */ HalfEdge* prevNode(); friend std::ostream& operator<< (std::ostream& os, const HalfEdge& el); static void toStringNode(const HalfEdge* he, std::ostream& os); }; } // namespace geos.edgegraph } // namespace geos