blob: 888c5e058661fbae531e5d60749f36d93cebbb5c [file] [log] [blame]
John Park91e69732019-03-03 13:12:43 -08001from constants import *
2import numpy as np
Ravago Jonesb170ed32022-06-01 21:16:15 -07003import scipy.optimize
John Park91e69732019-03-03 13:12:43 -08004from libspline import Spline, DistanceSpline, Trajectory
Ravago Jones56941a52021-07-31 14:42:38 -07005import copy
Ravago Jonesfa8da562022-07-02 18:10:22 -07006from dataclasses import dataclass
John Park91e69732019-03-03 13:12:43 -08007
Ravago Jones8da89c42022-07-17 19:34:06 -07008
Ravago Jonesfa8da562022-07-02 18:10:22 -07009@dataclass
10class ControlPointIndex:
11 """Class specifying the index of a control point"""
12
13 # The index of the multispline in the list of multisplines
14 multispline_index: int
15
16 # The index of the spline in the multispline
17 spline_index: int
18
19 # The index of the control point in the spline [0-5]
20 control_point_index: int
21
22
23class Multispline():
John Park91e69732019-03-03 13:12:43 -080024 def __init__(self):
Ravago Jonesfa8da562022-07-02 18:10:22 -070025 self.staged_points = [] # Holds all points not yet in spline
John Park91e69732019-03-03 13:12:43 -080026 self.libsplines = [] # Formatted for libspline library usage
27 self.splines = [] # Formatted for drawing
Ravago Jones3b92afa2021-02-05 14:27:32 -080028 self.constraints = [ # default constraints
29 {
30 "constraint_type": "LONGITUDINAL_ACCELERATION",
31 "value": 3
32 }, {
33 "constraint_type": "LATERAL_ACCELERATION",
34 "value": 2
35 }, {
36 "constraint_type": "VOLTAGE",
37 "value": 10
38 }
39 ]
John Park91e69732019-03-03 13:12:43 -080040
Ravago Jones56941a52021-07-31 14:42:38 -070041 def __deepcopy__(self, memo):
Ravago Jonesfa8da562022-07-02 18:10:22 -070042 new_copy = Multispline()
43 new_copy.staged_points = copy.deepcopy(self.staged_points, memo)
Ravago Jones56941a52021-07-31 14:42:38 -070044 new_copy.splines = copy.deepcopy(self.splines, memo)
45 new_copy.constraints = copy.deepcopy(self.constraints, memo)
46 new_copy.update_lib_spline()
47 return new_copy
48
John Park91e69732019-03-03 13:12:43 -080049 def getLibsplines(self):
50 return self.libsplines
51
Ravago Jones3b92afa2021-02-05 14:27:32 -080052 def setConstraint(self, id, value):
53 for constraint in self.constraints:
54 if constraint["constraint_type"] == id:
55 constraint["value"] = value
56 break
57
kyle96e46e12021-02-10 21:47:55 -080058 def getConstraint(self, id):
59 for constraint in self.constraints:
60 if constraint["constraint_type"] == id:
61 return constraint["value"]
62
Ravago Jones3b92afa2021-02-05 14:27:32 -080063 def addConstraintsToTrajectory(self, trajectory):
64 for constraint in self.constraints:
65 if constraint["constraint_type"] == "VOLTAGE":
66 trajectory.SetVoltageLimit(constraint["value"])
67 elif constraint["constraint_type"] == "LATERAL_ACCELERATION":
68 trajectory.SetLateralAcceleration(constraint["value"])
69 elif constraint["constraint_type"] == "LONGITUDINAL_ACCELERATION":
70 trajectory.SetLongitudinalAcceleration(constraint["value"])
71
John Park91e69732019-03-03 13:12:43 -080072 def splineExtrapolate(self, o_spline_edit):
73 spline_edit = o_spline_edit
74 if not spline_edit == len(self.splines) - 1:
John Park91e69732019-03-03 13:12:43 -080075 f = self.splines[spline_edit][5]
76 e = self.splines[spline_edit][4]
77 d = self.splines[spline_edit][3]
James Kuszmaulb33b07c2021-02-25 22:39:24 -080078 self.splines[spline_edit + 1][0] = f
79 self.splines[spline_edit + 1][1] = f * 2 + e * -1
80 self.splines[spline_edit + 1][2] = d + f * 4 + e * -4
John Park91e69732019-03-03 13:12:43 -080081
82 if not spline_edit == 0:
John Park91e69732019-03-03 13:12:43 -080083 a = self.splines[spline_edit][0]
84 b = self.splines[spline_edit][1]
85 c = self.splines[spline_edit][2]
James Kuszmaulb33b07c2021-02-25 22:39:24 -080086 self.splines[spline_edit - 1][5] = a
87 self.splines[spline_edit - 1][4] = a * 2 + b * -1
88 self.splines[spline_edit - 1][3] = c + a * 4 + b * -4
John Park91e69732019-03-03 13:12:43 -080089
90 def updates_for_mouse_move(self, index_of_edit, spline_edit, x, y, difs):
91 if index_of_edit > -1:
Ravago Jones36c92f02021-07-24 16:35:33 -070092 self.splines[spline_edit][index_of_edit] = [x, y]
John Park91e69732019-03-03 13:12:43 -080093
94 if index_of_edit == 5:
95 self.splines[spline_edit][
96 index_of_edit -
97 2] = self.splines[spline_edit][index_of_edit - 2] + difs
98 self.splines[spline_edit][
99 index_of_edit -
100 1] = self.splines[spline_edit][index_of_edit - 1] + difs
101
102 if index_of_edit == 0:
103 self.splines[spline_edit][
104 index_of_edit +
105 2] = self.splines[spline_edit][index_of_edit + 2] + difs
106 self.splines[spline_edit][
107 index_of_edit +
108 1] = self.splines[spline_edit][index_of_edit + 1] + difs
109
110 if index_of_edit == 4:
111 self.splines[spline_edit][
112 index_of_edit -
113 1] = self.splines[spline_edit][index_of_edit - 1] + difs
114
115 if index_of_edit == 1:
116 self.splines[spline_edit][
117 index_of_edit +
118 1] = self.splines[spline_edit][index_of_edit + 1] + difs
119
James Kuszmaulb33b07c2021-02-25 22:39:24 -0800120 self.splineExtrapolate(spline_edit)
John Park91e69732019-03-03 13:12:43 -0800121
122 def update_lib_spline(self):
123 self.libsplines = []
124 array = np.zeros(shape=(6, 2), dtype=float)
125 for points in self.splines:
126 for j, point in enumerate(points):
127 array[j, 0] = point[0]
128 array[j, 1] = point[1]
129 spline = Spline(np.ascontiguousarray(np.transpose(array)))
130 self.libsplines.append(spline)
131
Ravago Jonesfa8da562022-07-02 18:10:22 -0700132 @staticmethod
133 def nearest_distance(multisplines, point):
134 """Finds the spot along the multisplines that is closest to a
135 given point on the field
136
137 Returns the closest multispline and the distance along that multispline
138 """
Ravago Jonesb170ed32022-06-01 21:16:15 -0700139 def distance(t, distance_spline, point):
140 return np.sum((distance_spline.XY(t) - point)**2)
141
142 # We know the derivative of the function,
143 # so scipy doesn't need to compute it every time
144 def ddistance(t, distance_spline, point):
145 return np.sum(2 * (distance_spline.XY(t) - point) *
146 distance_spline.DXY(t))
147
Ravago Jonesb170ed32022-06-01 21:16:15 -0700148 best_result = None
Ravago Jonesfa8da562022-07-02 18:10:22 -0700149 best_multispline = None
Ravago Jonesb170ed32022-06-01 21:16:15 -0700150
Ravago Jonesfa8da562022-07-02 18:10:22 -0700151 for multispline_index, multispline in enumerate(multisplines):
152 distance_spline = DistanceSpline(multispline.getLibsplines())
Ravago Jonesb170ed32022-06-01 21:16:15 -0700153
Ravago Jonesfa8da562022-07-02 18:10:22 -0700154 # The optimizer finds local minima that often aren't what we want,
155 # so try from multiple locations to find a better minimum.
156 guess_points = np.linspace(0, distance_spline.Length(), num=5)
Ravago Jonesb170ed32022-06-01 21:16:15 -0700157
Ravago Jonesfa8da562022-07-02 18:10:22 -0700158 for guess in guess_points:
159 result = scipy.optimize.minimize(
160 distance,
161 guess,
162 args=(distance_spline, point),
163 bounds=((0, distance_spline.Length()), ),
164 jac=ddistance,
165 )
Ravago Jonesb170ed32022-06-01 21:16:15 -0700166
Ravago Jonesfa8da562022-07-02 18:10:22 -0700167 if result.success and (best_result == None
168 or result.fun < best_result.fun):
169 best_result = result
170 best_multispline = multispline
Ravago Jonesb170ed32022-06-01 21:16:15 -0700171
Ravago Jonesfa8da562022-07-02 18:10:22 -0700172 return (best_multispline, best_result)
173
174 def toJsonObject(self):
Ravago Jones3b92afa2021-02-05 14:27:32 -0800175 multi_spline = {
176 "spline_count": 0,
177 "spline_x": [],
178 "spline_y": [],
179 "constraints": self.constraints,
180 }
181 for points in self.splines:
182 multi_spline["spline_count"] += 1
183 for j, point in enumerate(points):
184 if j == 0 and multi_spline["spline_count"] > 1:
185 continue # skip overlapping points
186 multi_spline["spline_x"].append(point[0])
187 multi_spline["spline_y"].append(point[1])
188 return multi_spline
189
Ravago Jonesfa8da562022-07-02 18:10:22 -0700190 @staticmethod
191 def fromJsonObject(multi_spline):
192 multispline = Multispline()
193 multispline.constraints = multi_spline["constraints"]
194 multispline.splines = []
195 multispline.staged_points = []
Ravago Jones3b92afa2021-02-05 14:27:32 -0800196
197 i = 0
198 for j in range(multi_spline["spline_count"]):
199 # get the last point of the last spline
200 # and read in another 6 points
201 for i in range(i, i + 6):
Ravago Jonesfa8da562022-07-02 18:10:22 -0700202 multispline.staged_points.append(
Ravago Jones3b92afa2021-02-05 14:27:32 -0800203 [multi_spline["spline_x"][i], multi_spline["spline_y"][i]])
Ravago Jonesfa8da562022-07-02 18:10:22 -0700204 multispline.splines.append(np.array(multispline.staged_points))
205 multispline.staged_points = []
206 multispline.update_lib_spline()
207
208 return multispline
Ravago Jones3b92afa2021-02-05 14:27:32 -0800209
John Park91e69732019-03-03 13:12:43 -0800210 def getSplines(self):
211 return self.splines
212
Ravago Jonesfa8da562022-07-02 18:10:22 -0700213 def setControlPoint(self, index, x, y):
214 self.splines[index.spline_index][index.control_point_index] = [x, y]
John Park91e69732019-03-03 13:12:43 -0800215
Ravago Jonesfa8da562022-07-02 18:10:22 -0700216 def addPoint(self, x, y):
217 if (len(self.staged_points) < 6):
218 self.staged_points.append([x, y])
219 if (len(self.staged_points) == 6):
220 self.splines.append(np.array(self.staged_points))
221 self.staged_points = []
John Park91e69732019-03-03 13:12:43 -0800222 self.update_lib_spline()
223 return True
224
Ravago Jonesfa8da562022-07-02 18:10:22 -0700225 def extrapolate(self):
226 """Stages 3 points extrapolated from the end of the multispline"""
227 if len(self.getSplines()) < 1: return
228
229 self.staged_points = []
230
231 spline = self.getSplines()[-1]
232 point1 = spline[5]
233 point2 = spline[4]
234 point3 = spline[3]
235
236 self.staged_points.append(point1)
237 self.staged_points.append(point1 * 2 - point2)
238 self.staged_points.append(point3 + point1 * 4 - point2 * 4)