InterpolatePy 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,281 @@
1
+ import numpy as np
2
+
3
+
4
+ class LinearPath:
5
+ def __init__(self, pi: np.ndarray, pf: np.ndarray) -> None:
6
+ """
7
+ Initialize a linear path from point pi to point pf.
8
+
9
+ Parameters:
10
+ pi (array-like): Initial point coordinates [x, y, z]
11
+ pf (array-like): Final point coordinates [x, y, z]
12
+ """
13
+ self.pi: np.ndarray = np.array(pi)
14
+ self.pf: np.ndarray = np.array(pf)
15
+ self.length: float = np.linalg.norm(self.pf - self.pi)
16
+
17
+ # Unit tangent vector (constant for linear path)
18
+ if self.length > 0:
19
+ self.tangent: np.ndarray = (self.pf - self.pi) / self.length
20
+ else:
21
+ self.tangent = np.zeros(3)
22
+
23
+ def position(self, s: float) -> np.ndarray:
24
+ """
25
+ Calculate position at arc length s.
26
+
27
+ Parameters:
28
+ s (float or array): Arc length parameter(s)
29
+
30
+ Returns:
31
+ numpy.ndarray: Position vector(s)
32
+ """
33
+ # Ensure s is within valid range
34
+ s = np.clip(s, 0, self.length)
35
+
36
+ # Equation 4.34: p(s) = pi + (s/||pf-pi||)(pf-pi)
37
+ return self.pi + (s / self.length) * (self.pf - self.pi) if self.length > 0 else self.pi
38
+
39
+ def velocity(self, _s: float | None = None) -> np.ndarray:
40
+ """
41
+ Calculate first derivative with respect to arc length.
42
+ For linear path, this is constant and doesn't depend on s.
43
+
44
+ Parameters:
45
+ _s (float, optional): Arc length parameter (not used for linear path)
46
+
47
+ Returns:
48
+ numpy.ndarray: Velocity (tangent) vector
49
+ """
50
+ # Equation 4.35: dp/ds = (pf-pi)/||pf-pi||
51
+ return self.tangent
52
+
53
+ @staticmethod
54
+ def acceleration(_s: float | None = None) -> np.ndarray:
55
+ """
56
+ Calculate second derivative with respect to arc length.
57
+ For linear path, this is always zero.
58
+
59
+ Parameters:
60
+ _s (float, optional): Arc length parameter (not used for linear path)
61
+
62
+ Returns:
63
+ numpy.ndarray: Acceleration vector (always zero for linear path)
64
+ """
65
+ # Equation 4.36: d²p/ds² = 0
66
+ return np.zeros(3)
67
+
68
+ def evaluate_at(self, s_values: float | list[float] | np.ndarray) -> dict[str, np.ndarray]:
69
+ """
70
+ Evaluate position, velocity, and acceleration at specific arc length values.
71
+
72
+ Parameters:
73
+ s_values (float or array-like): Arc length parameter(s)
74
+
75
+ Returns:
76
+ dict: Dictionary containing arrays for position, velocity, and acceleration
77
+ Each array has shape (n, 3) where n is the number of s values
78
+ """
79
+ # Convert scalar to array if needed
80
+ s_values_arr: np.ndarray = (
81
+ np.array([s_values]) if np.isscalar(s_values) else np.array(s_values)
82
+ )
83
+
84
+ # Clip values to valid range
85
+ s_clipped = np.clip(s_values_arr, 0, self.length)
86
+
87
+ # Initialize result arrays
88
+ n = len(s_clipped)
89
+ positions = np.zeros((n, 3))
90
+ velocities = np.zeros((n, 3))
91
+ accelerations = np.zeros((n, 3))
92
+
93
+ # Calculate positions for each s
94
+ for i, s in enumerate(s_clipped):
95
+ positions[i] = self.position(s)
96
+
97
+ # For linear path, velocity is constant and acceleration is zero
98
+ velocities[:] = self.velocity()
99
+ # accelerations already initialized to zeros
100
+
101
+ return {
102
+ "position": positions,
103
+ "velocity": velocities,
104
+ "acceleration": accelerations,
105
+ "s": s_clipped,
106
+ }
107
+
108
+ def all_traj(self, num_points: int = 100) -> dict[str, np.ndarray]:
109
+ """
110
+ Generate a complete trajectory along the entire linear path.
111
+
112
+ Parameters:
113
+ num_points (int): Number of points to generate along the path
114
+
115
+ Returns:
116
+ dict: Dictionary containing arrays for position, velocity, and acceleration
117
+ Each array has shape (num_points, 3)
118
+ """
119
+ # Generate evenly spaced points along the entire path
120
+ s_values = np.linspace(0, self.length, num_points)
121
+
122
+ # Use evaluate_at to get the trajectory data
123
+ return self.evaluate_at(s_values)
124
+
125
+
126
+ class CircularPath:
127
+ def __init__(self, r: np.ndarray, d: np.ndarray, pi: np.ndarray) -> None:
128
+ """
129
+ Initialize a circular path.
130
+
131
+ Parameters:
132
+ r (array-like): Unit vector of circle axis
133
+ d (array-like): Position vector of a point on the circle axis
134
+ pi (array-like): Position vector of a point on the circle
135
+ """
136
+ self.r: np.ndarray = np.array(r)
137
+ self.d: np.ndarray = np.array(d)
138
+ self.pi: np.ndarray = np.array(pi)
139
+
140
+ # Normalize axis vector
141
+ self.r /= np.linalg.norm(self.r)
142
+
143
+ # Compute delta vector
144
+ delta = self.pi - self.d
145
+
146
+ # Check if pi is not on the axis
147
+ if np.abs(np.dot(delta, self.r)) >= np.linalg.norm(delta):
148
+ raise ValueError("The point pi must not be on the circle axis")
149
+
150
+ # Compute center (equation 4.37)
151
+ self.center = self.d + np.dot(delta, self.r) * self.r
152
+
153
+ # Compute radius
154
+ self.radius = np.linalg.norm(self.pi - self.center)
155
+
156
+ # Compute rotation matrix
157
+ x_prime = (self.pi - self.center) / self.radius # Unit vector in x' direction
158
+ z_prime = self.r # Unit vector in z' direction
159
+ y_prime = np.cross(z_prime, x_prime) # Unit vector in y' direction
160
+
161
+ # Rotation matrix R = [x' y' z']
162
+ self.R = np.column_stack((x_prime, y_prime, z_prime))
163
+
164
+ def position(self, s: float | np.ndarray) -> np.ndarray:
165
+ """
166
+ Calculate position at arc length s.
167
+
168
+ Parameters:
169
+ s (float or array): Arc length parameter(s)
170
+
171
+ Returns:
172
+ numpy.ndarray: Position vector(s)
173
+ """
174
+ if np.isscalar(s):
175
+ # Position in local coordinate system (equation 4.38)
176
+ p_prime = np.array(
177
+ [self.radius * np.cos(s / self.radius), self.radius * np.sin(s / self.radius), 0]
178
+ )
179
+
180
+ # Position in global coordinate system (equation 4.39)
181
+ return self.center + self.R @ p_prime
182
+
183
+ # Ensure s is treated as an array/iterable
184
+ s_array = np.asarray(s)
185
+ positions = []
186
+ for s_val in s_array:
187
+ p_prime = np.array(
188
+ [
189
+ self.radius * np.cos(s_val / self.radius),
190
+ self.radius * np.sin(s_val / self.radius),
191
+ 0,
192
+ ]
193
+ )
194
+ positions.append(self.center + self.R @ p_prime)
195
+ return np.array(positions)
196
+
197
+ def velocity(self, s: float) -> np.ndarray:
198
+ """
199
+ Calculate first derivative with respect to arc length.
200
+
201
+ Parameters:
202
+ s (float): Arc length parameter
203
+
204
+ Returns:
205
+ numpy.ndarray: Velocity (tangent) vector
206
+ """
207
+ # Velocity in local coordinate system (equation 4.40)
208
+ dp_prime_ds = np.array([-np.sin(s / self.radius), np.cos(s / self.radius), 0])
209
+
210
+ # Velocity in global coordinate system
211
+ return self.R @ dp_prime_ds
212
+
213
+ def acceleration(self, s: float) -> np.ndarray:
214
+ """
215
+ Calculate second derivative with respect to arc length.
216
+
217
+ Parameters:
218
+ s (float): Arc length parameter
219
+
220
+ Returns:
221
+ numpy.ndarray: Acceleration vector
222
+ """
223
+ # Acceleration in local coordinate system (equation 4.41)
224
+ d2p_prime_ds2 = np.array(
225
+ [-np.cos(s / self.radius) / self.radius, -np.sin(s / self.radius) / self.radius, 0]
226
+ )
227
+
228
+ # Acceleration in global coordinate system
229
+ return self.R @ d2p_prime_ds2
230
+
231
+ def evaluate_at(self, s_values: float | list[float] | np.ndarray) -> dict[str, np.ndarray]:
232
+ """
233
+ Evaluate position, velocity, and acceleration at specific arc length values.
234
+
235
+ Parameters:
236
+ s_values (float or array-like): Arc length parameter(s)
237
+
238
+ Returns:
239
+ dict: Dictionary containing arrays for position, velocity, and acceleration
240
+ Each array has shape (n, 3) where n is the number of s values
241
+ """
242
+ # Convert scalar to array if needed
243
+ s_values_arr: np.ndarray = (
244
+ np.array([s_values]) if np.isscalar(s_values) else np.array(s_values)
245
+ )
246
+
247
+ # Initialize result arrays
248
+ n = len(s_values_arr)
249
+ positions = np.zeros((n, 3))
250
+ velocities = np.zeros((n, 3))
251
+ accelerations = np.zeros((n, 3))
252
+
253
+ # Calculate values for each s
254
+ for i, s in enumerate(s_values_arr):
255
+ positions[i] = self.position(s)
256
+ velocities[i] = self.velocity(s)
257
+ accelerations[i] = self.acceleration(s)
258
+
259
+ return {
260
+ "position": positions,
261
+ "velocity": velocities,
262
+ "acceleration": accelerations,
263
+ "s": s_values_arr,
264
+ }
265
+
266
+ def all_traj(self, num_points: int = 100) -> dict[str, np.ndarray]:
267
+ """
268
+ Generate a complete trajectory around the entire circular path.
269
+
270
+ Parameters:
271
+ num_points (int): Number of points to generate around the circle
272
+
273
+ Returns:
274
+ dict: Dictionary containing arrays for position, velocity, and acceleration
275
+ Each array has shape (num_points, 3)
276
+ """
277
+ # Generate evenly spaced points for a complete circle
278
+ s_values = np.linspace(0, 2 * np.pi * self.radius, num_points)
279
+
280
+ # Use evaluate_at to get the trajectory data
281
+ return self.evaluate_at(s_values)