Brick
Loading...
Searching...
No Matches
spline_path.h
1#pragma once
2
3#include <algorithm>
4#include <functional>
5#include <memory>
6#include <vector>
7
8#include "core/math/spline/cubic_hermite_spline.h"
9#include "core/math/spline/quintic_hermite_spline.h"
10
11/**
12 * @brief Manages multi-segment Hermite spline paths.
13 */
15 public:
16 /** @brief Order of spline polynomials used per segment. */
17 enum class Order {
18 Cubic = 3, ///< Cubic Hermite splines (C1 continuity)
19 Quintic = 5, ///< Quintic Hermite splines (C2 continuity)
20 };
21
22 /** @brief Default constructor. */
23 SplinePath() = default;
24
25 /**
26 * @brief Constructs a multi-segment path using a custom Spline factory.
27 * @param points Boundary waypoints.
28 * @param factory Factory closure producing SplineBase segments.
29 * @param max_err Maximum allowable interpolation error in inches for the arc-length lookup table.
30 * @return Constructed SplinePath instance.
31 */
33 const std::vector<HermitePoint>& points,
34 std::function<std::unique_ptr<SplineBase>(const HermitePoint&, const HermitePoint&, double)>
35 factory,
36 double max_err = 1e-4
37 ) {
38 SplinePath path;
39 if (points.size() < 2 || !factory) {
40 return path;
41 }
42
43 path.segments_.reserve(points.size() - 1);
44 path.segment_starts_.reserve(points.size() - 1);
45
46 double accum = 0.0;
47 for (size_t i = 0; i + 1 < points.size(); ++i) {
48 std::unique_ptr<SplineBase> segment = factory(points[i], points[i + 1], max_err);
49 if (!segment) continue;
50
51 path.segment_starts_.push_back(accum);
52 accum += segment->length();
53 path.segments_.push_back(std::move(segment));
54 }
55 path.total_length_ = accum;
56 return path;
57 }
58
59 /**
60 * @brief Constructs a multi-segment path from a vector of Hermite waypoints.
61 * @param points Hermite waypoints defining path positions, tangents, and optional second
62 * derivatives.
63 * @param order Spline order (Cubic or Quintic).
64 * @param max_err Maximum allowable interpolation error in inches for the arc-length lookup table.
65 * @return Constructed SplinePath instance.
66 */
68 const std::vector<HermitePoint>& points, Order order = Order::Quintic, double max_err = 1e-4
69 ) {
70 return from_waypoints(
71 points,
72 [order](
73 const HermitePoint& start, const HermitePoint& end, double max_err
74 ) -> std::unique_ptr<SplineBase> {
75 if (order == Order::Cubic) {
76 return std::unique_ptr<SplineBase>(new CubicHermiteSpline(start, end, max_err));
77 } else {
78 return std::unique_ptr<SplineBase>(new QuinticHermiteSpline(start, end, max_err));
79 }
80 },
81 max_err
82 );
83 }
84
85 /** @return True if the path contains no segments. */
86 bool empty() const { return segments_.empty(); }
87
88 /** @return Number of spline segments in the path. */
89 size_t segment_count() const { return segments_.size(); }
90
91 /** @return Total arc length of the multi-segment path in inches. */
92 double length() const { return total_length_; }
93
94 /**
95 * @brief Samples complete state at arc distance s along the multi-segment path.
96 * @param s Arc distance in inches.
97 * @return SplineSample struct containing position, velocity, acceleration, heading, and
98 * curvature.
99 */
100 SplineSample sample_by_s(double s) const {
101 if (segments_.empty()) {
102 return {};
103 }
104 if (s <= 0.0) {
105 SplineSample out = segments_.front()->sample_by_s(0.0);
106 out.s = 0.0;
107 return out;
108 }
109 if (s >= total_length_) {
110 SplineSample out = segments_.back()->sample_by_s(segments_.back()->length());
111 out.s = total_length_;
112 return out;
113 }
114
115 const size_t idx = segment_index_for_s(s);
116 const double local_s = s - segment_starts_[idx];
117 SplineSample out = segments_[idx]->sample_by_s(local_s);
118 out.s = s;
119 return out;
120 }
121
122 /**
123 * @brief Samples 2D positions uniformly spaced by distance ds along the path.
124 * @param ds Distance step in inches.
125 * @return Vector of 2D translation points.
126 */
127 std::vector<Translation2d> sample_by_spacing(double ds) const {
128 std::vector<Translation2d> points;
129 if (segments_.empty() || ds <= 0.0) {
130 return points;
131 }
132 for (double s = 0.0; s < total_length_; s += ds) {
133 points.push_back(sample_by_s(s).position);
134 }
135 points.push_back(sample_by_s(total_length_).position);
136 return points;
137 }
138
139 /**
140 * @brief Samples 2D positions with fixed number of steps per spline segment.
141 * @param steps_per_segment Number of steps per segment.
142 * @return Vector of 2D translation points.
143 */
144 std::vector<Translation2d> sample_by_segment_steps(double steps_per_segment) const {
145 std::vector<Translation2d> points;
146 if (segments_.empty() || steps_per_segment <= 0.0) {
147 return points;
148 }
149
150 const int sample_count = static_cast<int>(steps_per_segment);
151 if (sample_count <= 0) {
152 return points;
153 }
154
155 for (size_t i = 0; i < segments_.size(); ++i) {
156 for (int step = 0; step < sample_count; ++step) {
157 const double u = static_cast<double>(step) / static_cast<double>(sample_count);
158 points.push_back(segments_[i]->position(u));
159 }
160 }
161 points.push_back(segments_.back()->position(1.0));
162 return points;
163 }
164
165 private:
166 size_t segment_index_for_s(double s) const {
167 auto upper = std::upper_bound(segment_starts_.begin(), segment_starts_.end(), s);
168 if (upper == segment_starts_.begin()) {
169 return 0;
170 }
171 size_t idx = static_cast<size_t>((upper - segment_starts_.begin()) - 1);
172 if (idx >= segments_.size()) {
173 idx = segments_.size() - 1;
174 }
175 return idx;
176 }
177
178 std::vector<std::unique_ptr<SplineBase>> segments_;
179 std::vector<double> segment_starts_;
180 double total_length_ = 0.0;
181};
CubicHermiteSpline()=default
Default constructor.
QuinticHermiteSpline()=default
Default constructor.
static SplinePath from_hermite(const std::vector< HermitePoint > &points, Order order=Order::Quintic, double max_err=1e-4)
Constructs a multi-segment path from a vector of Hermite waypoints.
Definition spline_path.h:67
SplinePath()=default
Default constructor.
double length() const
Definition spline_path.h:92
bool empty() const
Definition spline_path.h:86
static SplinePath from_waypoints(const std::vector< HermitePoint > &points, std::function< std::unique_ptr< SplineBase >(const HermitePoint &, const HermitePoint &, double)> factory, double max_err=1e-4)
Constructs a multi-segment path using a custom Spline factory.
Definition spline_path.h:32
std::vector< Translation2d > sample_by_spacing(double ds) const
Samples 2D positions uniformly spaced by distance ds along the path.
Definition spline_path.h:127
SplineSample sample_by_s(double s) const
Samples complete state at arc distance s along the multi-segment path.
Definition spline_path.h:100
std::vector< Translation2d > sample_by_segment_steps(double steps_per_segment) const
Samples 2D positions with fixed number of steps per spline segment.
Definition spline_path.h:144
Order
Order of spline polynomials used per segment.
Definition spline_path.h:17
@ Quintic
Quintic Hermite splines (C2 continuity).
Definition spline_path.h:19
@ Cubic
Cubic Hermite splines (C1 continuity).
Definition spline_path.h:18
size_t segment_count() const
Definition spline_path.h:89
Represents a boundary point along a Hermite spline.
Definition hermite_point.h:16
Evaluated state sample at a specific parameter u or distance s along a spline.
Definition spline_base.h:14
double s
Arc length along spline from start in inches.
Definition spline_base.h:16