/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file NBContHelper.h14/// @author Daniel Krajzewicz15/// @author Jakob Erdmann16/// @author Michael Behrisch17/// @date Mon, 17 Dec 200118///19// Some methods for traversing lists of edges20/****************************************************************************/21#pragma once22#include <config.h>2324#include <vector>25#include <iostream>26#include <cmath>27#include <algorithm>28#include <cassert>29#include "NBHelpers.h"30#include "NBCont.h"31#include "NBEdge.h"32#include "NBNode.h"33#include <utils/common/StdDefs.h>34#include <utils/geom/GeomHelper.h>353637// ===========================================================================38// class definitions39// ===========================================================================40/**41* NBContHelper42* Some static helper methods that traverse a sorted list of edges in both43* directions44*/45class NBContHelper {46public:47/** Moves the given iterator clockwise within the given container48of edges sorted clockwise */49static void nextCW(const EdgeVector& edges,50EdgeVector::const_iterator& from);5152/** Moves the given iterator counter clockwise within the given container53of edges sorted clockwise */54static void nextCCW(const EdgeVector& edges,55EdgeVector::const_iterator& from);5657static double getMaxSpeed(const EdgeVector& edges);5859static double getMinSpeed(const EdgeVector& edges);6061/** writes the vector of bools to the given stream */62static std::ostream& out(std::ostream& os, const std::vector<bool>& v);636465/**66* relative_outgoing_edge_sorter67* Class to sort edges by their angle in relation to the node the68* edge using this class is incoming into. This is normally done to69* sort edges outgoing from the node the using edge is incoming in70* by their angle in relation to the using edge's angle (this angle71* is the reference angle).72*/73class relative_outgoing_edge_sorter {74public:75/// constructor76explicit relative_outgoing_edge_sorter(NBEdge* e) : myAngle(e->getEndAngle()) {}77/// constructor78explicit relative_outgoing_edge_sorter(double angle) : myAngle(angle) {}7980public:81/// comparing operation82bool operator()(const NBEdge* e1, const NBEdge* e2) const;8384private:85/// @brief the reference angle to compare edges agains86double myAngle;87};888990/**91* relative_incoming_edge_sorter92* Class to sort edges by their angle in relation to an outgoing edge.93* This is normally done to sort edges incoming at the starting node of this edge94* by their angle in relation to the using edge's angle (this angle95* is the reference angle).96*/97class relative_incoming_edge_sorter {98public:99/// constructor100explicit relative_incoming_edge_sorter(NBEdge* e) : myAngle(e->getStartAngle()) {}101/// constructor102explicit relative_incoming_edge_sorter(double angle) : myAngle(angle) {}103104public:105/// comparing operation106bool operator()(const NBEdge* e1, const NBEdge* e2) const;107108private:109/// @brief the reference angle to compare edges agains110double myAngle;111};112113114/**115* edge_by_priority_sorter116* Class to sort edges by their priority117*/118class edge_by_priority_sorter {119public:120edge_by_priority_sorter(SVCPermissions permissions) : myPermissions(permissions) {}121/// comparing operator122int operator()(NBEdge* e1, NBEdge* e2) const {123if (e1->getPriority() != e2->getPriority()) {124return e1->getPriority() > e2->getPriority();125}126if (e1->getSpeed() != e2->getSpeed()) {127return e1->getSpeed() > e2->getSpeed();128}129return e1->getNumLanesThatAllow(myPermissions, false) > e2->getNumLanesThatAllow(myPermissions, false);130}131132private:133SVCPermissions myPermissions;134};135136// ---------------------------137138/**139* @class edge_opposite_direction_sorter140* @brief Class to sort edges by their angle in relation to the given edge141*142* The resulting sorted list has the edge in the most opposite direction143* to the given edge as her first entry.144*/145class edge_opposite_direction_sorter {146public:147/** @brief Constructor148* @param[in] e The edge to which the sorting relates149* @param[in] n The node to consider150*/151explicit edge_opposite_direction_sorter(const NBEdge* const e, const NBNode* const n, bool regardPriority) :152myNode(n),153myEdge(e),154myRegardPriority(regardPriority) {155myAngle = getEdgeAngleAt(e, n);156}157158/** @brief Comparing operation159* @param[in] e1 The first edge to compare160* @param[in] e2 The second edge to compare161* @return Which edge is more opposite to the related one162*/163int operator()(NBEdge* e1, NBEdge* e2) const {164if (!myRegardPriority || e1->getPriority() == e2->getPriority() || e1 == myEdge || e2 == myEdge) {165return getDiff(e1) > getDiff(e2);166} else {167return e1->getPriority() > e2->getPriority();168}169}170171protected:172/** @brief Computes the angle difference between the related and the given edge173* @param[in] e The edge to compare the angle difference of174* @return The angle difference175*/176double getDiff(const NBEdge* const e) const {177return fabs(GeomHelper::angleDiff(getEdgeAngleAt(e, myNode), myAngle));178}179180/** @brief Returns the given edge's angle at the given node181*182* Please note that we always consider the "outgoing direction".183* @param[in] e The edge to which the sorting relates184* @param[in] n The node to consider185*/186double getEdgeAngleAt(const NBEdge* const e, const NBNode* const n) const {187if (e->getFromNode() == n) {188return e->getGeometry().angleAt2D(0);189} else {190return e->getGeometry()[-1].angleTo2D(e->getGeometry()[-2]);191}192}193194private:195196/// @brief The related node197const NBNode* const myNode;198199/// @brief the reference edge200const NBEdge* const myEdge;201202/// @brief The angle of the related edge at the given node203double myAngle;204205/// @brief Whether edge priority may override closer angles206bool myRegardPriority;207208};209210// ---------------------------211212/**213* edge_similar_direction_sorter214* Class to sort edges by their angle in relation to the given edge215* The resulting list should have the edge in the most similar direction216* to the given edge as its first entry217*/218class edge_similar_direction_sorter {219public:220/// constructor221explicit edge_similar_direction_sorter(const NBEdge* const e, bool outgoing = true) :222myCompareOutgoing(outgoing),223myAngle(outgoing ? e->getShapeEndAngle() : e->getShapeStartAngle())224{}225226/// comparing operation227int operator()(const NBEdge* e1, const NBEdge* e2) const {228const double d1 = angleDiff(myCompareOutgoing ? e1->getShapeStartAngle() : e1->getShapeEndAngle(), myAngle);229const double d2 = angleDiff(myCompareOutgoing ? e2->getShapeStartAngle() : e2->getShapeEndAngle(), myAngle);230if (fabs(fabs(d1) - fabs(d2)) < NUMERICAL_EPS) {231if (fabs(d1 - d2) > NUMERICAL_EPS) {232return d1 < d2;233} else {234return e1->getNumericalID() < e2->getNumericalID();235}236}237return fabs(d1) < fabs(d2);238}239240private:241double angleDiff(const double angle1, const double angle2) const {242double d = angle2 - angle1;243while (d >= 180.) {244d -= 360.;245}246while (d < -180.) {247d += 360.;248}249return d;250}251252253private:254/// the angle to find the edge with the opposite direction255bool myCompareOutgoing;256double myAngle;257};258259260/**261* @class node_with_incoming_finder262*/263class node_with_incoming_finder {264public:265/// constructor266node_with_incoming_finder(const NBEdge* const e);267268bool operator()(const NBNode* const n) const;269270private:271const NBEdge* const myEdge;272273};274275276/**277* @class node_with_outgoing_finder278*/279class node_with_outgoing_finder {280public:281/// constructor282node_with_outgoing_finder(const NBEdge* const e);283284bool operator()(const NBNode* const n) const;285286private:287const NBEdge* const myEdge;288289};290291292293294class edge_with_destination_finder {295public:296/// constructor297edge_with_destination_finder(NBNode* dest);298299bool operator()(NBEdge* e) const;300301private:302NBNode* myDestinationNode;303304private:305/// @brief invalidated assignment operator306edge_with_destination_finder& operator=(const edge_with_destination_finder& s);307308};309310311/** Tries to return the first edge within the given container which312connects both given nodes */313static NBEdge* findConnectingEdge(const EdgeVector& edges,314NBNode* from, NBNode* to);315316317/** returns the maximum speed allowed on the edges */318static double maxSpeed(const EdgeVector& ev);319320/**321* same_connection_edge_sorter322* This class is used to sort edges which connect the same nodes.323* The edges are sorted in dependence to edges connecting them. The324* rightmost will be the first in the list; the leftmost the last one.325*/326class same_connection_edge_sorter {327public:328/// constructor329explicit same_connection_edge_sorter() { }330331/// comparing operation332int operator()(NBEdge* e1, NBEdge* e2) const {333std::pair<double, double> mm1 = getMinMaxRelAngles(e1);334std::pair<double, double> mm2 = getMinMaxRelAngles(e2);335if (mm1.first == mm2.first && mm1.second == mm2.second) {336// ok, let's simply sort them arbitrarily337return e1->getID() < e2->getID();338}339340assert(341(mm1.first <= mm2.first && mm1.second <= mm2.second)342||343(mm1.first >= mm2.first && mm1.second >= mm2.second));344return (mm1.first >= mm2.first && mm1.second >= mm2.second);345}346347/**348*349*/350std::pair<double, double> getMinMaxRelAngles(NBEdge* e) const {351double min = 360;352double max = 360;353const EdgeVector& ev = e->getConnectedEdges();354for (EdgeVector::const_iterator i = ev.begin(); i != ev.end(); ++i) {355double angle = NBHelpers::normRelAngle(356e->getTotalAngle(), (*i)->getTotalAngle());357if (min == 360 || min > angle) {358min = angle;359}360if (max == 360 || max < angle) {361max = angle;362}363}364return std::pair<double, double>(min, max);365}366};367368369friend std::ostream& operator<<(std::ostream& os, const EdgeVector& ev);370371class opposite_finder {372public:373/// constructor374opposite_finder(NBEdge* edge)375: myReferenceEdge(edge) { }376377bool operator()(NBEdge* e) const {378return e->isTurningDirectionAt(myReferenceEdge) ||379myReferenceEdge->isTurningDirectionAt(e);380}381382private:383NBEdge* myReferenceEdge;384385};386387/**388* edge_by_angle_to_nodeShapeCentroid_sorter389* Class to sort edges by their angle in relation to the node shape390* */391class edge_by_angle_to_nodeShapeCentroid_sorter {392public:393/// constructor394explicit edge_by_angle_to_nodeShapeCentroid_sorter(const NBNode* n) : myNode(n) {}395396public:397/// comparing operation398bool operator()(const NBEdge* e1, const NBEdge* e2) const;399400private:401/// the edge to compute the relative angle of402const NBNode* myNode;403};404405};406407408