CARLA
 
载入中...
搜索中...
未找到
GraphParser.cpp
浏览该文件的文档.
1// 版权所有 (c) 巴塞罗那自治大学 (UAB) 的 2023 计算机视觉中心 (CVC)。
2//
3// 本作品根据 MIT 许可证的条款进行许可。
4// 有关副本,请参阅 <https://opensource.org/licenses/MIT>。
5
6#include "Carla.h"
7#include "GraphParser.h"
9#include <type_traits>
10#include <unordered_set>
11
12// 定义命名空间MapGen,以下代码中的类型、函数、类等都在这个命名空间范围内
13namespace MapGen {
14
15 // 使用DoublyConnectedEdgeList类型定义一个别名Graph,方便后续代码使用该类型时书写更简洁
16 using Graph = DoublyConnectedEdgeList;
17
18 // ===========================================================================
19 // -- Local static methods ---------------------------------------------------
20 // ===========================================================================
21
22 // 静态函数,根据给定的角度(以弧度为单位)确定其所在的象限(将圆周划分为四个象限,每个象限角度范围是PI/2)
23 // 通过将角度除以HALF_PI(假设HALF_PI已定义为PI/2)并四舍五入取整来得到象限编号(0 - 3)
24 static int getQuadrant(float angle) {
25 return static_cast<int>(std::round(angle/HALF_PI));
26 }
27
28 // 静态函数,计算两个角度之间的旋转角度(假设输入的角度大致相隔PI/2)
29 // 先获取两个角度中较小和较大角度对应的象限编号,然后根据象限关系计算旋转角度,旋转角度是PI/2的整数倍
30 static float getRotation(float angle0, float angle1) {
31 const int min = getQuadrant(std::min(angle0, angle1));
32 const int max = getQuadrant(std::max(angle0, angle1));
33 return HALF_PI * std::min(min, min * max);
34 }
35
36 // 静态函数,计算三个角度之间的旋转角度(假设输入的角度大致相隔PI/2)
37 // 通过获取三个角度对应的象限编号之和,利用switch语句根据不同的和值情况返回相应的旋转角度(PI/2、0、-PI/2、PI等)
38 // 如果出现意外的象限组合情况,会输出错误日志并返回0.0
39 static float getRotation(float angle0, float angle1, float angle2) {
40 /// @todo There has to be a better way.
41 switch (getQuadrant(angle0) + getQuadrant(angle1) + getQuadrant(angle2)) {
42 case 0:
43 return HALF_PI;
44 case 1:
45 return 0.0;
46 case 2:
47 return -1.0 * HALF_PI;
48 case 3:
49 return PI;
50 default:
51 UE_LOG(LogCarla, Error, TEXT("Wrong quadrants"));
52 return 0.0;
53 }
54 }
55
56 // 静态函数,用于修正图的数据(对输入的图结构Graph进行一些属性的设置和调整)
57 // 遍历图中的每个节点,计算每个节点相关的角度信息,根据节点连接的边的数量设置节点的旋转角度、交点类型等属性
58 static void fixGraphData(Graph &graph) {
59 // 遍历图中的每个节点
60 for (auto &node : graph.GetNodes()) {
61 std::vector<float> angles;
62 angles.reserve(4u);
63 // 获取该节点的第一条出边(半边缘)
64 auto &firstEdge = Graph::GetLeavingHalfEdge(node);
65 auto *edge = &firstEdge;
66 // 循环遍历该节点的所有出边(半边缘),形成一个环的遍历
67 do {
68 // 获取并设置当前边的角度属性
69 edge->Angle = Graph::GetAngle(*edge);
70 angles.emplace_back(edge->Angle);
71 // 获取下一条在该节点的边(半边缘)
72 edge = &Graph::GetNextInNode(*edge);
73 } while (edge!= &firstEdge);
74 // 确保角度列表不为空(理论上正常情况下不会为空,这里是一种检查机制)
75 check(!angles.empty());
76 node.EdgeCount = angles.size();
77 node.bIsIntersection = true;
78 // 根据节点连接的边的数量(2、3、4或其他情况)来设置节点的旋转角度和交点类型
79 switch (node.EdgeCount) {
80 case 2:
81 node.Rotation = getRotation(angles[0u], angles[1u]);
82 node.IntersectionType = EIntersectionType::Turn90Deg;
83 break;
84 case 3:
85 node.Rotation = getRotation(angles[0u], angles[1u], angles[2u]);
86 node.IntersectionType = EIntersectionType::TIntersection;
87 break;
88 case 4:
89 default:
90 node.Rotation = 0.0;
91 node.IntersectionType = EIntersectionType::XIntersection;
92 break;
93 }
94 node.Rots.swap(angles);
95 }
96 }
97
98 // ===========================================================================
99 // -- RoadSegmentBuilder -----------------------------------------------------
100 // ===========================================================================
101
102 // RoadSegmentBuilder类,用于构建道路路段相关的描述信息
104 public:
105 // 存储道路路段描述的智能指针向量,每个元素指向一个RoadSegmentDescription类型的对象
106 std::vector<TUniquePtr<RoadSegmentDescription>> Segments;
107
108 // 构造函数,接受一个图结构(Graph类型)作为参数,用于初始化构建器关联的图
109 explicit RoadSegmentBuilder(const Graph &graph) : _graph(graph) {}
110
111 // 向构建器中添加一个半边缘(HalfEdge),根据一些条件决定是否插入,并处理道路路段的构建逻辑
112 void Add(Graph::HalfEdge &edge) {
113 if (!insert(edge))
114 return;
115 // 如果该半边缘的源节点是一个交点,则创建一个新的道路路段描述对象,并标记当前不是处理初始状态了
116 if (Graph::GetSource(edge).bIsIntersection) {
117 Segments.emplace_back(MakeUnique<RoadSegmentDescription>());
118 _handlingInitial = false;
119 }
120 // 如果当前处于处理初始状态,则将该半边缘指针添加到初始边的向量中
121 if (_handlingInitial) {
122 _initial.emplace_back(&edge);
123 } else {
124 // 否则将该半边缘添加到当前正在构建的道路路段描述对象中
125 Segments.back()->Add(edge);
126 }
127 }
128
129 // 关闭当前正在构建的道路路段,将初始边添加到当前路段描述对象中,并重置处理初始状态的标记为true
130 void Close() {
131 for (auto edge : _initial) {
132 Segments.back()->Add(*edge);
133 }
134 _handlingInitial = true;
135 }
136
137 private:
138 // 私有函数,用于插入半边缘及其对应的配对半边缘到已访问边的集合中
139 // 如果这两条边都还没有被访问过,则插入成功并返回true,否则返回false
141 return _visitedEdges.insert(&edge).second &&
142 _visitedEdges.insert(&Graph::GetPair(edge)).second;
143 }
144
145 // 存储构建器关联的图结构,不可修改(通过const修饰)
146 const Graph &_graph;
147 // 用于记录已经访问过的半边缘的集合,避免重复处理边
148 std::unordered_set<const Graph::HalfEdge *> _visitedEdges;
149 // 标记是否处于处理初始状态,初始化为true
150 bool _handlingInitial = true;
151 // 存储初始的半边缘指针的向量,用于在关闭道路路段时添加到路段描述中
152 std::vector<const Graph::HalfEdge *> _initial;
153 };
154
155 // ===========================================================================
156 // -- GraphParser ------------------------------------------------------------
157 // ===========================================================================
158
159 // GraphParser类的构造函数,用于解析图结构并构建相关的城市区域和道路路段描述信息
161 // 进行一些前置条件检查,确保图中的节点数量、半边缘数量、面的数量满足一定要求
162 check(graph.CountNodes() >= 4u);
163 check(graph.CountHalfEdges() >= 8u);
164 check(graph.CountFaces() >= 2u);
165
166 // 调用fixGraphData函数修正图的数据,设置节点相关属性
167 fixGraphData(graph);
168
169 // 为存储城市区域描述的向量预先分配空间,数量是图的面数量减1(可能排除了某个特殊的面,比如无界的面)
170 CityAreas.reserve(graph.CountFaces() - 1);
171
172 // 创建RoadSegmentBuilder对象,传入图结构,用于后续构建道路路段描述
173 RoadSegmentBuilder rsb(graph);
174
175 // 获取图的面的列表
176 auto faceList = graph.GetFaces();
177 auto it = faceList.begin();
178 ++it; // 忽略第一个面(可能是无界的面,具体取决于图的定义和使用场景)
179 // 遍历除第一个面之外的每个面
180 for (; it!= faceList.end(); ++it) {
181 // 创建一个新的城市区域描述对象,并添加到城市区域描述向量中
182 CityAreas.emplace_back(MakeUnique<CityAreaDescription>(*it));
183 CityAreaDescription &cityArea = *CityAreas.back();
184 // 获取该面的第一条半边缘
185 auto &firstEdge = Graph::GetHalfEdge(*it);
186 // 遍历该面的所有半边缘(形成一个环的遍历)
187 for (auto *edge = &Graph::GetNextInFace(firstEdge);
188 edge!= &firstEdge;
189 edge = &Graph::GetNextInFace(*edge)) {
190 // 将半边缘的源节点添加到城市区域描述对象中
191 cityArea.Add(Graph::GetSource(*edge));
192 // 将半边缘添加到RoadSegmentBuilder中,用于构建道路路段描述
193 rsb.Add(*edge);
194 }
195 // 关闭当前正在构建的道路路段
196 rsb.Close();
197 }
198
199 // 将RoadSegmentBuilder中构建好的道路路段描述向量交换到GraphParser类自身的RoadSegments成员变量中
200 RoadSegments.swap(rsb.Segments);
201 }
202
203} // namespace MapGen
UE_LOG(LogCarla, Log, TEXT("UActorDispatcher::Destroying actor: '%s' %x"), *Id, Actor)
double min(double v1, double v2)
返回两个数中的较小值
Definition Simplify.h:591
void Add(const GraphNode &Node)
简单的双连通边链表结构。它只允许添加元素,不允许删除元素。
static float GetAngle(const HalfEdge &halfEdge)
返回 half-edge 的角度,范围为 [-pi, pi]
static HalfEdge & GetHalfEdge(Face &face)
static HalfEdge & GetLeavingHalfEdge(Node &node)
static HalfEdge & GetPair(HalfEdge &halfEdge)
static Node & GetSource(HalfEdge &halfEdge)
static HalfEdge & GetNextInNode(HalfEdge &halfEdge)
static HalfEdge & GetNextInFace(HalfEdge &halfEdge)
RoadSegmentList RoadSegments
Definition GraphParser.h:99
GraphParser(DoublyConnectedEdgeList &Dcel)
CityAreaList CityAreas
RoadSegmentBuilder(const Graph &graph)
std::unordered_set< const Graph::HalfEdge * > _visitedEdges
void Add(Graph::HalfEdge &edge)
std::vector< const Graph::HalfEdge * > _initial
std::vector< TUniquePtr< RoadSegmentDescription > > Segments
bool insert(Graph::HalfEdge &edge)
static int getQuadrant(float angle)
static void fixGraphData(Graph &graph)
DoublyConnectedEdgeList Graph
static float getRotation(float angle0, float angle1)