Skip to content

Commit 91c081a

Browse files
committed
Refactor discrete path
1 parent 9b0edf3 commit 91c081a

File tree

4 files changed

+209
-199
lines changed

4 files changed

+209
-199
lines changed

CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ add_executable(RaidZeroLib
135135
# include/RaidZeroLib/api/Utility/CrossPlatformThread.hpp
136136
# include/RaidZeroLib/api/Utility/CrossPlatformMutex.hpp
137137
include/RaidZeroLib/api/Utility/Math.hpp
138-
# include/RaidZeroLib/api/Pathing/DiscretePath.hpp
138+
include/RaidZeroLib/api/Pathing/DiscretePath.hpp
139139
# include/RaidZeroLib/api/Pathing/ParametricPath.hpp
140140
# include/RaidZeroLib/api/Pathing/CubicBezier.hpp
141141
# include/RaidZeroLib/api/Pathing/PiecewiseCubicBezier.hpp
@@ -160,7 +160,7 @@ add_executable(RaidZeroLib
160160
src/RaidZeroLib/api/Utility/Math.cpp
161161
# src/RaidZeroLib/api/Utility/CrossPlatformThread.cpp
162162
# src/RaidZeroLib/api/Pathing/ParametricPath.cpp
163-
# src/RaidZeroLib/api/Pathing/DiscretePath.cpp
163+
src/RaidZeroLib/api/Pathing/DiscretePath.cpp
164164
# src/RaidZeroLib/api/Pathing/CubicBezier.cpp
165165
# src/RaidZeroLib/api/Pathing/PiecewiseCubicBezier.cpp
166166
# src/RaidZeroLib/api/Filter/SlewRate.cpp
@@ -179,7 +179,7 @@ add_executable(RaidZeroLib
179179
test/test/Geometry/TransformTest.cpp
180180
test/test/Geometry/PoseTest.cpp
181181

182-
# test/test/Pathing/DiscretePathTest.cpp
182+
test/test/Pathing/DiscretePathTest.cpp
183183
# test/test/Pathing/ParametricPathTest.cpp
184184
# test/test/Pathing/CubicBezierTest.cpp
185185
# test/test/Pathing/PiecewiseCubicBezierTest.cpp
Lines changed: 122 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,138 @@
11
#pragma once
22
#include "RaidZeroLib/api/Geometry/Point.hpp"
3-
#include <algorithm>
4-
#include <exception>
53
#include <vector>
4+
#include <ranges>
5+
#include <concepts>
6+
#include <cassert>
67

78
namespace rz {
8-
using namespace okapi;
99

10+
11+
/**
12+
* @brief Immutable sequence of 2D waypoints with unit-safe geometry.
13+
*
14+
* `DiscretePath` stores a read-only ordered list of `Point` (meters). It supports:
15+
* - Construction from an initializer list or any C++20 range of points,
16+
* - Concatenation with another path or a single point,
17+
* - Const iteration (forward and reverse),
18+
* - Random access and front/back access,
19+
* - Geometric queries such as signed curvature at an interior vertex.
20+
*
21+
* @invariant Paths are **non-empty**. All constructors assert `size() > 0`.
22+
* @note This type is immutable: iteration returns const iterators and no mutating
23+
* accessors are exposed.
24+
*/
1025
class DiscretePath {
1126
public:
12-
DiscretePath() = default;
13-
DiscretePath(const std::initializer_list<Point>& waypoint);
14-
DiscretePath(const std::vector<Point>& waypoint);
15-
~DiscretePath() = default;
27+
using value_type = Point;
28+
using const_reference = const Point&;
29+
using container_type = std::vector<Point>;
30+
using size_type = std::size_t;
31+
using const_iterator = std::vector<Point>::const_iterator;
32+
using const_reverse_iterator = std::vector<Point>::const_reverse_iterator;
33+
34+
/**
35+
* @brief Constructs a path from a brace-initialized list of points.
36+
*
37+
* @param waypoints Non-empty list of waypoints (meters).
38+
* @pre `waypoints.size() > 0` (debug-checked).
39+
*/
40+
explicit DiscretePath(std::initializer_list<Point> waypoints);
1641

42+
/**
43+
* @brief Constructs a path from any C++20 input range of points.
44+
*
45+
* Accepts containers and views whose element/reference type is convertible to `Point`,
46+
* e.g. `std::vector<Point>`, `std::list<Point>`, raw arrays, or range pipelines
47+
* like `points | std::views::filter(...)`.
48+
*
49+
* @tparam Range A `std::ranges::input_range` whose reference is convertible to `Point`.
50+
* @param range Non-empty range of waypoints (meters).
51+
* @pre `std::ranges::distance(range) > 0` (debug-checked after construction).
52+
* @note This constructor is disabled for `Range = DiscretePath` to avoid hijacking copy/move.
53+
*/
54+
template <std::ranges::input_range Range>
55+
requires (
56+
!std::same_as<std::remove_cvref_t<Range>, DiscretePath> &&
57+
std::convertible_to<std::ranges::range_reference_t<Range>, Point>
58+
)
59+
explicit DiscretePath(Range&& range)
60+
: path(std::ranges::begin(range), std::ranges::end(range)) {
61+
assert(!path.empty() && "DiscretePath cannot be empty");
62+
}
63+
64+
/**
65+
* @brief Concatenates two paths (this followed by @p rhs).
66+
*
67+
* @param rhs Path to append.
68+
* @return New path containing `*this` then `rhs`.
69+
*/
1770
DiscretePath operator+(const DiscretePath& rhs) const;
71+
72+
/**
73+
* @brief Appends a single waypoint to the end of the path.
74+
*
75+
* @param rhs Point to append (meters).
76+
* @return New path with @p rhs appended.
77+
*/
1878
DiscretePath operator+(const Point& rhs) const;
19-
DiscretePath& operator+=(const DiscretePath& rhs);
20-
DiscretePath& operator+=(const Point& rhs);
21-
22-
std::vector<Point>::iterator begin();
23-
std::vector<Point>::const_iterator begin() const;
24-
std::vector<Point>::iterator end();
25-
std::vector<Point>::const_iterator end() const;
26-
27-
std::vector<Point>::reverse_iterator rbegin();
28-
std::vector<Point>::const_reverse_iterator rbegin() const;
29-
std::vector<Point>::reverse_iterator rend();
30-
std::vector<Point>::const_reverse_iterator rend() const;
31-
32-
Point& operator[](int index);
33-
const Point& operator[](int index) const;
34-
Point& front();
35-
const Point& front() const;
36-
Point& back();
37-
const Point& back() const;
38-
QCurvature getCurvature(int index) const;
39-
int size() const;
79+
80+
/// @brief Forward begin iterator.
81+
const_iterator begin() const noexcept;
82+
83+
/// @brief Forward end iterator (one past the last).
84+
const_iterator end() const noexcept;
85+
86+
/// @brief Reverse begin iterator (last element).
87+
const_reverse_iterator rbegin() const noexcept;
88+
89+
/// @brief Reverse end iterator (one before the first).
90+
const_reverse_iterator rend() const noexcept;
91+
92+
/**
93+
* @brief Random access without bounds checking.
94+
* @param index Zero-based index in `[0, size())`.
95+
* @return Reference to the indexed point.
96+
*/
97+
const_reference operator[](size_type index) const noexcept;
98+
99+
/// @brief First waypoint.
100+
const_reference front() const noexcept;
101+
102+
/// @brief Last waypoint.
103+
const_reference back() const noexcept;
104+
105+
/**
106+
* @brief Signed curvature at vertex @p index using the circumcircle of three consecutive points.
107+
*
108+
* For an interior index `i` (i.e. `1 ≤ i ≤ size()-2`), let
109+
* `R = circumradius(P[i-1], P[i], P[i+1])`. The unsigned curvature is `κ = 1/R`.
110+
* The sign follows the triangle orientation (left turn positive, right turn negative).
111+
* Endpoints and collinear triples yield 0.
112+
*
113+
* @param index Vertex index at which to evaluate curvature.
114+
* @return Curvature in 1/meters (`au::QuantityD<au::Inverse<au::Meters>>`).
115+
*/
116+
au::QuantityD<au::Inverse<au::Meters>> getCurvature(std::size_t index) const noexcept;
117+
118+
/// @brief Number of waypoints.
119+
size_type size() const noexcept;
40120

41121
private:
42122
std::vector<Point> path;
43123
};
44124

45-
std::vector<Translation>::const_iterator closestPoint(std::vector<Translation>::const_iterator begin,
46-
std::vector<Translation>::const_iterator end, const Point& point);
47-
} // namespace rz
125+
/**
126+
* @brief Iterator-based closest-point search by Euclidean distance.
127+
*
128+
* Returns an iterator to the element in `[begin, end)` minimizing `distTo(point)`.
129+
* If the range is empty, returns `end`.
130+
*
131+
* @param begin Const iterator to the first waypoint.
132+
* @param end Const iterator one past the last waypoint.
133+
* @param point Query point (meters).
134+
* @return Iterator to the closest waypoint, or @p end if the range is empty.
135+
*/
136+
DiscretePath::const_iterator closestPoint(DiscretePath::const_iterator begin,
137+
DiscretePath::const_iterator end, const Point& point);
138+
} // namespace rz
Lines changed: 35 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,80 @@
11
#include "RaidZeroLib/api/Pathing/DiscretePath.hpp"
2+
#include <algorithm>
23

34
namespace rz {
4-
DiscretePath::DiscretePath(const std::initializer_list<Point>& waypoint) : path(waypoint) {
5-
}
6-
7-
DiscretePath::DiscretePath(const std::vector<Point>& waypoint) : path(waypoint) {
5+
DiscretePath::DiscretePath(std::initializer_list<Point> waypoints) : path(waypoints) {
6+
assert(!path.empty() && "DiscretePath cannot be empty");
87
}
98

109
DiscretePath DiscretePath::operator+(const DiscretePath& rhs) const {
11-
DiscretePath result(path);
12-
result.path.insert(result.path.end(), rhs.path.begin(), rhs.path.end());
13-
return result;
14-
}
15-
16-
DiscretePath DiscretePath::operator+(const Point& rhs) const {
17-
DiscretePath result(path);
18-
result.path.emplace_back(rhs);
19-
return result;
20-
}
21-
22-
DiscretePath& DiscretePath::operator+=(const DiscretePath& rhs) {
23-
path.insert(path.end(), rhs.path.begin(), rhs.path.end());
24-
return *this;
25-
}
26-
27-
DiscretePath& DiscretePath::operator+=(const Point& rhs) {
28-
path.emplace_back(rhs);
29-
return *this;
30-
}
10+
std::vector<Point> result;
11+
result.reserve(size() + rhs.size());
3112

32-
std::vector<Point>::iterator DiscretePath::begin() {
33-
return path.begin();
34-
}
35-
36-
std::vector<Point>::iterator DiscretePath::end() {
37-
return path.end();
13+
result.insert(result.end(), path.begin(), path.end());
14+
result.insert(result.end(), rhs.path.begin(), rhs.path.end());
15+
return DiscretePath(result);
3816
}
3917

40-
std::vector<Point>::reverse_iterator DiscretePath::rbegin() {
41-
return path.rbegin();
42-
}
18+
DiscretePath DiscretePath::operator+(const Point& rhs) const {
19+
std::vector<Point> result;
20+
result.reserve(size() + 1);
4321

44-
std::vector<Point>::reverse_iterator DiscretePath::rend() {
45-
return path.rend();
22+
result.insert(result.end(), path.begin(), path.end());
23+
result.push_back(rhs);
24+
return DiscretePath(result);
4625
}
4726

48-
std::vector<Point>::const_iterator DiscretePath::begin() const {
27+
DiscretePath::const_iterator DiscretePath::begin() const noexcept {
4928
return path.begin();
5029
}
5130

52-
std::vector<Point>::const_iterator DiscretePath::end() const {
31+
DiscretePath::const_iterator DiscretePath::end() const noexcept {
5332
return path.end();
5433
}
5534

56-
std::vector<Point>::const_reverse_iterator DiscretePath::rbegin() const {
35+
DiscretePath::const_reverse_iterator DiscretePath::rbegin() const noexcept {
5736
return path.rbegin();
5837
}
5938

60-
std::vector<Point>::const_reverse_iterator DiscretePath::rend() const {
39+
DiscretePath::const_reverse_iterator DiscretePath::rend() const noexcept {
6140
return path.rend();
6241
}
6342

64-
Point& DiscretePath::operator[](int index) {
65-
return path[index];
66-
}
67-
68-
const Point& DiscretePath::operator[](int index) const {
43+
const Point& DiscretePath::operator[](std::size_t index) const noexcept {
6944
return path[index];
7045
}
7146

72-
Point& DiscretePath::front() {
47+
const Point& DiscretePath::front() const noexcept {
7348
return path.front();
7449
}
7550

76-
const Point& DiscretePath::front() const {
77-
return path.front();
78-
}
79-
80-
Point& DiscretePath::back() {
51+
const Point& DiscretePath::back() const noexcept {
8152
return path.back();
8253
}
8354

84-
const Point& DiscretePath::back() const {
85-
return path.back();
55+
std::size_t DiscretePath::size() const noexcept {
56+
return path.size();
8657
}
8758

88-
int DiscretePath::size() const {
89-
return (int)path.size();
90-
}
91-
92-
QCurvature DiscretePath::getCurvature(int index) const {
93-
if (index <= 0 || index >= (int)path.size() - 1) {
94-
return 0 * radpm;
59+
au::QuantityD<au::Inverse<au::Meters>> DiscretePath::getCurvature(std::size_t index) const noexcept {
60+
if (index == 0 || index >= path.size() - 1) {
61+
return au::ZERO;
9562
}
9663

97-
QLength radius = circumradius(path[index - 1], path[index], path[index + 1]);
64+
const auto radius = circumradius(path[index - 1], path[index], path[index + 1]);
9865

99-
if (std::isnan(radius.getValue()) || radius.getValue() == 0) {
100-
return 0 * radpm;
66+
if (radius == au::ZERO) {
67+
return au::ZERO;
10168
}
10269

103-
return 1 * radian / radius;
70+
return 1.0 / radius;
10471
}
10572

106-
std::vector<Translation>::const_iterator closestPoint(std::vector<Translation>::const_iterator begin,
107-
std::vector<Translation>::const_iterator end,
108-
const Point& point) {
109-
const auto comparison = [point](const Point& a, const Point& b) { return a.distTo(point) < b.distTo(point); };
110-
111-
return std::min_element(begin, end, comparison);
73+
DiscretePath::const_iterator closestPoint(DiscretePath::const_iterator begin,
74+
DiscretePath::const_iterator end,
75+
const Point& point) {
76+
const auto proj = [&point](const Point& p) { return p.distTo(point); };
77+
return std::ranges::min_element(begin, end, std::less<>{}, proj);
11278
}
11379

11480
} // namespace rz

0 commit comments

Comments
 (0)