Skip to content

Commit 0603aac

Browse files
committed
Make rest of pathing use au, improve parametric path approximation
1 parent 91c081a commit 0603aac

File tree

10 files changed

+536
-281
lines changed

10 files changed

+536
-281
lines changed

CMakeLists.txt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ add_executable(RaidZeroLib
136136
# include/RaidZeroLib/api/Utility/CrossPlatformMutex.hpp
137137
include/RaidZeroLib/api/Utility/Math.hpp
138138
include/RaidZeroLib/api/Pathing/DiscretePath.hpp
139-
# include/RaidZeroLib/api/Pathing/ParametricPath.hpp
140-
# include/RaidZeroLib/api/Pathing/CubicBezier.hpp
141-
# include/RaidZeroLib/api/Pathing/PiecewiseCubicBezier.hpp
139+
include/RaidZeroLib/api/Pathing/ParametricPath.hpp
140+
include/RaidZeroLib/api/Pathing/CubicBezier.hpp
141+
include/RaidZeroLib/api/Pathing/PiecewiseCubicBezier.hpp
142142
# include/RaidZeroLib/api/Trajectory/MotionProfile/TrapezoidalMotionProfile.hpp
143143
# include/RaidZeroLib/api/Trajectory/MotionProfile/MotionProfile.hpp
144144
# include/RaidZeroLib/api/Filter/SlewRate.hpp
@@ -159,10 +159,10 @@ add_executable(RaidZeroLib
159159
# src/RaidZeroLib/api/Utility/CrossPlatformMutex.cpp
160160
src/RaidZeroLib/api/Utility/Math.cpp
161161
# src/RaidZeroLib/api/Utility/CrossPlatformThread.cpp
162-
# src/RaidZeroLib/api/Pathing/ParametricPath.cpp
162+
src/RaidZeroLib/api/Pathing/ParametricPath.cpp
163163
src/RaidZeroLib/api/Pathing/DiscretePath.cpp
164-
# src/RaidZeroLib/api/Pathing/CubicBezier.cpp
165-
# src/RaidZeroLib/api/Pathing/PiecewiseCubicBezier.cpp
164+
src/RaidZeroLib/api/Pathing/CubicBezier.cpp
165+
src/RaidZeroLib/api/Pathing/PiecewiseCubicBezier.cpp
166166
# src/RaidZeroLib/api/Filter/SlewRate.cpp
167167
src/RaidZeroLib/api/Geometry/Twist.cpp
168168
src/RaidZeroLib/api/Geometry/Point.cpp
@@ -180,9 +180,9 @@ add_executable(RaidZeroLib
180180
test/test/Geometry/PoseTest.cpp
181181

182182
test/test/Pathing/DiscretePathTest.cpp
183-
# test/test/Pathing/ParametricPathTest.cpp
184-
# test/test/Pathing/CubicBezierTest.cpp
185-
# test/test/Pathing/PiecewiseCubicBezierTest.cpp
183+
test/test/Pathing/ParametricPathTest.cpp
184+
test/test/Pathing/CubicBezierTest.cpp
185+
test/test/Pathing/PiecewiseCubicBezierTest.cpp
186186

187187
# test/test/Units/UnitsTest.cpp
188188

Lines changed: 104 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,126 @@
11
#pragma once
22
#include "RaidZeroLib/api/Geometry/Point.hpp"
33
#include "RaidZeroLib/api/Pathing/ParametricPath.hpp"
4-
#include "RaidZeroLib/api/Units/Units.hpp"
54

65
namespace rz {
7-
using namespace okapi;
86

7+
/**
8+
* @brief Cubic Bézier parametric path segment.
9+
*
10+
* A `CubicBezier` represents a smooth parametric curve defined by four control points:
11+
* \f[
12+
* P(t) = (1-t)^3 c_0 + 3(1-t)^2 t c_1 + 3(1-t)t^2 c_2 + t^3 c_3, \quad t \in [0, 1].
13+
* \f]
14+
*
15+
* Each segment is constructed from two knots, which encode both position
16+
* and tangent direction/length at their endpoints. The constructor expands those into
17+
* the four cubic control points:
18+
* - `c₀ = start.getPoint()`
19+
* - `c₁ = start.getForwardControl()`
20+
* - `c₂ = end.getBackwardControl()`
21+
* - `c₃ = end.getPoint()`
22+
*
23+
* **Continuity:** When consecutive segments share the same end/start `Knot`,
24+
* the resulting chain is automatically C⁰ and C¹ continuous at the join.
25+
*
26+
* @see PiecewiseCubicBezier
27+
*/
928
class CubicBezier : public ParametricPath {
1029
public:
30+
/**
31+
* @brief Control knot defining both position and tangent handle geometry.
32+
*
33+
* Each `Knot` specifies a point `(x, y)` in meters, an outgoing tangent
34+
* direction `theta` in radians, and a tangent handle length `magnitude` in meters.
35+
* From these, two derived “handles” are computed:
36+
* - Forward control: `point + magnitude * [cos(theta), sin(theta)]`
37+
* - Backward control: `point - magnitude * [cos(theta), sin(theta)]`
38+
*
39+
* These handles are used when constructing Bézier segments:
40+
* the *start* control point contributes its **forward** handle,
41+
* and the *end* control point contributes its **backward** handle.
42+
*/
1143
class Knot {
1244
public:
13-
Knot(QLength x, QLength y, QAngle theta, QLength magnitude);
45+
/**
46+
* @brief Constructs a control knot with position, tangent angle, and handle length.
47+
*
48+
* @param x X coordinate (meters).
49+
* @param y Y coordinate (meters).
50+
* @param theta Tangent direction (radians).
51+
* @param magnitude Length of tangent handle (meters).
52+
*
53+
* @pre `magnitude > 0`.
54+
*/
55+
Knot(au::QuantityD<au::Meters> x, au::QuantityD<au::Meters> y,
56+
au::QuantityD<au::Radians> theta, au::QuantityD<au::Meters> magnitude) noexcept;
1457

15-
Point getPoint() const;
58+
/// @brief Returns the anchor position (x, y) of the control knot.
59+
Point getPoint() const noexcept;
1660

17-
Point getForwardControl() const;
61+
/// @brief Returns the forward (outgoing) control handle in world coordinates.
62+
Point getForwardControl() const noexcept;
1863

19-
Point getBackwardControl() const;
64+
/// @brief Returns the backward (incoming) control handle in world coordinates.
65+
Point getBackwardControl() const noexcept;
2066

2167
private:
22-
QLength x;
23-
QLength y;
24-
QAngle theta;
25-
QLength magnitude;
68+
au::QuantityD<au::Meters> x;
69+
au::QuantityD<au::Meters> y;
70+
au::QuantityD<au::Radians> theta;
71+
au::QuantityD<au::Meters> magnitude;
2672
};
2773

28-
CubicBezier(Knot start, Knot end);
74+
/**
75+
* @brief Constructs a cubic Bézier segment between two control knots.
76+
*
77+
* The start and end control points define the endpoints and tangents.
78+
* Internally, the four Bézier control points are expanded as:
79+
* ```
80+
* c0 = start.getPoint();
81+
* c1 = start.getForwardControl();
82+
* c2 = end.getBackwardControl();
83+
* c3 = end.getPoint();
84+
* ```
85+
*
86+
* @param start Start control knot.
87+
* @param end End control knot.
88+
*/
89+
CubicBezier(const Knot& start, const Knot& end) noexcept;
2990

30-
Point getPoint(double t) const override;
91+
/**
92+
* @brief Computes the position P(t) along the cubic Bézier.
93+
*
94+
* @param t Normalized parameter ∈ [0, 1].
95+
* @return 2D point (meters).
96+
*/
97+
Point getPoint(double t) const noexcept override;
3198

32-
Point getVelocity(double t) const override;
99+
/**
100+
* @brief Computes the first derivative dP/dt at parameter t.
101+
*
102+
* The derivative is given by:
103+
* \f[
104+
* P'(t) = 3(1-t)^2 (c_1 - c_0) + 6(1-t)t (c_2 - c_1) + 3t^2 (c_3 - c_2)
105+
* \f]
106+
*
107+
* @param t Normalized parameter ∈ [0, 1].
108+
* @return Velocity vector (meters per unit t).
109+
*/
110+
Point getVelocity(double t) const noexcept override;
33111

34-
Point getAcceleration(double t) const override;
112+
/**
113+
* @brief Computes the second derivative d²P/dt² at parameter t.
114+
*
115+
* The expression is:
116+
* \f[
117+
* P''(t) = 6(1-t)(c_2 - 2c_1 + c_0) + 6t(c_3 - 2c_2 + c_1)
118+
* \f]
119+
*
120+
* @param t Normalized parameter ∈ [0, 1].
121+
* @return Acceleration vector (meters per unit t²).
122+
*/
123+
Point getAcceleration(double t) const noexcept override;
35124

36125
private:
37126
Point c0;
@@ -40,4 +129,4 @@ class CubicBezier : public ParametricPath {
40129
Point c3;
41130
};
42131

43-
} // namespace rz
132+
} // namespace rz
Lines changed: 116 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,125 @@
11
#pragma once
22
#include "RaidZeroLib/api/Pathing/DiscretePath.hpp"
3+
#include "au/au.hpp"
34

45
namespace rz {
5-
using namespace okapi;
66

7+
8+
/**
9+
* @brief Differentiable 2D path over a unit-interval parameter t ∈ [0, 1].
10+
*
11+
* Implementers provide position and its first two derivatives with respect to the
12+
* *dimensionless* path parameter @p t. Higher-level queries—heading, curvature,
13+
* arclength, parameter stepping by distance, and discretization—are derived here.
14+
*
15+
* **Units & semantics**
16+
* - `getPoint(t)` → position in meters (`Point` stores meter-valued components).
17+
* - `getVelocity(t)` → dP/dt in meters (derivative w.r.t. parameter, not time).
18+
* - `getAcceleration(t)` → d²P/dt² in meters (w.r.t. parameter).
19+
* - `getTheta(t)` → heading of the tangent (radians).
20+
* - `getCurvature(t)` → signed curvature κ = wedge(dP/dt, d²P/dt²) / |dP/dt|³ (1/meters).
21+
* - `getLength(a,b)` → ∫_a^b |dP/dt| dt (meters).
22+
*
23+
* **Domain**: All functions expect 0 ≤ t ≤ 1 (implementations may clamp internally).
24+
*
25+
* **Continuity**: For stable curvature, the path should be at least C¹ and piecewise C².
26+
*/
727
class ParametricPath {
828
public:
9-
virtual QLength getX(double t) const;
10-
11-
virtual QLength getY(double t) const;
12-
13-
virtual QLength getdX(double t) const;
14-
15-
virtual QLength getdY(double t) const;
16-
17-
virtual QLength getddX(double t) const;
18-
19-
virtual QLength getddY(double t) const;
20-
21-
virtual Point getPoint(double t) const = 0;
22-
23-
virtual Point getVelocity(double t) const = 0;
24-
25-
virtual Point getAcceleration(double t) const = 0;
26-
27-
virtual Rotation getTheta(double t) const;
28-
29-
virtual QCurvature getCurvature(double t) const;
30-
31-
virtual QLength getLength(double tStart = 0, double tEnd = 1) const;
32-
33-
virtual DiscretePath toDiscrete(int numSegments, bool end = true) const;
34-
35-
virtual DiscretePath toDiscrete(QLength distance, bool end = true) const;
36-
37-
virtual double stepT(double t, QLength distance) const;
29+
/// @brief Virtual destructor.
30+
virtual ~ParametricPath() = default;
31+
32+
/**
33+
* @brief Position on the path at parameter @p t.
34+
*
35+
* @param t Dimensionless parameter, typically in [0, 1].
36+
* @return 2D point in meters.
37+
*
38+
* @pre 0 ≤ t ≤ 1.
39+
*/
40+
virtual Point getPoint(double t) const noexcept = 0;
41+
42+
/**
43+
* @brief First derivative dP/dt at parameter @p t (w.r.t. path parameter).
44+
*
45+
* @param t Dimensionless parameter.
46+
* @return Tangent vector; magnitude has units of meters.
47+
*
48+
* @note This is not velocity w.r.t. physical time.
49+
*
50+
* @pre 0 ≤ t ≤ 1.
51+
*/
52+
virtual Point getVelocity(double t) const noexcept = 0;
53+
54+
/**
55+
* @brief Second derivative d²P/dt² at parameter @p t (w.r.t. path parameter).
56+
*
57+
* @param t Dimensionless parameter.
58+
* @return Second derivative vector; components in meters.
59+
*
60+
* @pre 0 ≤ t ≤ 1.
61+
*/
62+
virtual Point getAcceleration(double t) const noexcept = 0;
63+
64+
/**
65+
* @brief Path heading: orientation of the tangent dP/dt.
66+
*
67+
* Equivalent to `atan2(dy/dt, dx/dt)` via `Point::theta()`.
68+
*
69+
* @param t Dimensionless parameter.
70+
* @return Heading angle in radians.
71+
*
72+
* @pre 0 ≤ t ≤ 1.
73+
*
74+
* @note Returns 0 when |dP/dt| ≈ 0 (stationary/cusp).
75+
*/
76+
virtual au::QuantityD<au::Radians> getTheta(double t) const noexcept;
77+
78+
79+
/**
80+
* @brief Signed curvature κ(t) = wedge(dP/dt, d²P/dt²) / |dP/dt|³.
81+
*
82+
* Positive for left-turning, negative for right-turning (right-handed coordinates).
83+
*
84+
* @param t Dimensionless parameter.
85+
* @return Curvature in 1/meters.
86+
*
87+
* @pre 0 ≤ t ≤ 1.
88+
*
89+
* @note Returns 0 when |dP/dt| is ~0 to avoid division by zero.
90+
*/
91+
virtual au::QuantityD<au::Inverse<au::Meters>> getCurvature(double t) const noexcept;
92+
93+
/**
94+
* @brief Arclength along the path between parameters @p tStart and @p tEnd.
95+
*
96+
* Computes s = ∫_{tStart}^{tEnd} |dP/dt| dt using adaptive Gauss–Legendre quadrature.
97+
* https://en.wikipedia.org/wiki/Adaptive_quadrature
98+
*
99+
* @param tStart Start parameter (default 0).
100+
* @param tEnd End parameter (default 1).
101+
* @return Length in meters.
102+
*
103+
* @pre 0 ≤ tStart ≤ tEnd ≤ 1.
104+
*/
105+
virtual au::QuantityD<au::Meters> getLength(double tStart = 0, double tEnd = 1) const noexcept;
106+
107+
/**
108+
* @brief Advance the parameter by a target arclength measured along the path.
109+
*
110+
* Finds t' ≥ t such that ∫_{t}^{t'} |dP/dt| dt ≈ @p distance. Uses a safeguarded
111+
* Newton/bisection refinement with bracketing and clamps to t' ≤ 1.
112+
* https://discourse.julialang.org/t/references-and-implementation-for-safe-hybrid-univariate-newtons-method/87638
113+
*
114+
* @param t Current parameter in [0, 1].
115+
* @param distance Desired arclength step (meters), s ≥ 0.
116+
* @return Next parameter t' ∈ [t, 1].
117+
*
118+
* @note If @p distance == 0 or t == 1, returns 1.
119+
*/
120+
virtual double stepT(double t, au::QuantityD<au::Meters> distance) const noexcept;
121+
122+
virtual DiscretePath toDiscrete(au::QuantityD<au::Meters> distance, bool includeEnd = true) const;
38123
};
39124

40-
} // namespace rz
125+
} // namespace rz

0 commit comments

Comments
 (0)