mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-04 11:33:47 +08:00
Batch path gradient interpolation
This commit is contained in:
@@ -356,7 +356,8 @@ class ModelRenderer(Widget):
|
||||
path_pts = self._path.projected_points + np.array([self._rect.x, self._rect.y], dtype=np.float32)
|
||||
max_len = min(len(path_pts) // 2, len(self._acceleration_x))
|
||||
|
||||
segment_colors = []
|
||||
hues = []
|
||||
saturations = []
|
||||
gradient_stops = []
|
||||
|
||||
i = 0
|
||||
@@ -371,23 +372,24 @@ class ModelRenderer(Widget):
|
||||
lin_grad_point = 1 - (track_y - self._rect.y) / self._rect.height
|
||||
|
||||
# speed up: 120, slow down: 0
|
||||
path_hue = np.clip(60 + self._acceleration_x[i] * 35, 0, 120)
|
||||
path_hue = 60 + self._acceleration_x[i] * 35
|
||||
|
||||
saturation = min(abs(self._acceleration_x[i] * 1.5), 1)
|
||||
lightness = np.interp(saturation, [0.0, 1.0], [0.95, 0.62])
|
||||
alpha = np.interp(lin_grad_point, [0.75 / 2.0, 0.75], [0.4, 0.0])
|
||||
|
||||
# Use HSL to RGB conversion
|
||||
color = self._hsla_to_color(path_hue / 360.0, saturation, lightness, alpha)
|
||||
|
||||
hues.append(path_hue)
|
||||
saturations.append(saturation)
|
||||
gradient_stops.append(lin_grad_point)
|
||||
segment_colors.append(color)
|
||||
|
||||
# Skip a point, unless next is last
|
||||
i += 1 + (1 if (i + 2) < max_len else 0)
|
||||
|
||||
# Store the gradient in the path object
|
||||
self._exp_gradient.colors = segment_colors
|
||||
if gradient_stops:
|
||||
hues = np.clip(hues, 0, 120) / 360.0
|
||||
lightness = np.interp(saturations, [0.0, 1.0], [0.95, 0.62])
|
||||
alphas = np.interp(gradient_stops, [0.75 / 2.0, 0.75], [0.4, 0.0])
|
||||
self._exp_gradient.colors = [self._hsla_to_color(h, s, l, a)
|
||||
for h, s, l, a in zip(hues, saturations, lightness, alphas, strict=True)]
|
||||
else:
|
||||
self._exp_gradient.colors = []
|
||||
self._exp_gradient.stops = gradient_stops
|
||||
|
||||
|
||||
|
||||
@@ -80,3 +80,81 @@ class TestModelLineEndpoint:
|
||||
expected = renderer._map_line_to_polygon(expanded, 0.9, 1.2, 2, distance)
|
||||
actual = renderer._map_line_to_polygon(line, 0.9, 1.2, 1, distance)
|
||||
np.testing.assert_allclose(actual, expected, rtol=1e-6, atol=1e-6)
|
||||
|
||||
|
||||
class TestExperimentalGradient:
|
||||
@staticmethod
|
||||
def renderer(dtype, count, seed):
|
||||
from openpilot.selfdrive.ui.mici.onroad.model_renderer import ModelRenderer
|
||||
import pyray as rl
|
||||
rng = np.random.default_rng(seed)
|
||||
result = object.__new__(ModelRenderer)
|
||||
result._experimental_mode = True
|
||||
result._rect = rl.Rectangle(-12, 21, 536, 240)
|
||||
result._path = SimpleNamespace(projected_points=rng.uniform(-200, 500, (count, 2)).astype(dtype))
|
||||
result._acceleration_x = rng.uniform(-3, 3, 33).astype(dtype)
|
||||
result._exp_gradient = SimpleNamespace(colors=[], stops=[])
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def reference(renderer):
|
||||
points = renderer._path.projected_points + np.array([renderer._rect.x, renderer._rect.y], dtype=np.float32)
|
||||
end = min(len(points) // 2, len(renderer._acceleration_x))
|
||||
colors, stops = [], []
|
||||
index = 0
|
||||
while index < end:
|
||||
y = points[index][1]
|
||||
if y < renderer._rect.y or y > renderer._rect.y + renderer._rect.height:
|
||||
index += 1
|
||||
continue
|
||||
stop = 1 - (y - renderer._rect.y) / renderer._rect.height
|
||||
hue = np.clip(60 + renderer._acceleration_x[index] * 35, 0, 120)
|
||||
saturation = min(abs(renderer._acceleration_x[index] * 1.5), 1)
|
||||
lightness = np.interp(saturation, [0.0, 1.0], [0.95, 0.62])
|
||||
alpha = np.interp(stop, [0.75 / 2.0, 0.75], [0.4, 0.0])
|
||||
color = renderer._hsla_to_color(hue / 360.0, saturation, lightness, alpha)
|
||||
colors.append((color.r, color.g, color.b, color.a))
|
||||
stops.append(stop)
|
||||
index += 1 + (1 if index + 2 < end else 0)
|
||||
return colors, stops
|
||||
|
||||
@pytest.mark.parametrize('dtype', [np.float32, np.float64])
|
||||
@pytest.mark.parametrize('count', [0, 1, 2, 7, 33, 66])
|
||||
@pytest.mark.parametrize('seed', [0, 1, 9])
|
||||
def test_batched_gradient_matches_original_scalar_colors_and_clipping(self, dtype, count, seed):
|
||||
renderer = self.renderer(dtype, count, seed)
|
||||
expected_colors, expected_stops = self.reference(renderer)
|
||||
renderer._update_experimental_gradient()
|
||||
assert [(c.r, c.g, c.b, c.a) for c in renderer._exp_gradient.colors] == expected_colors
|
||||
assert renderer._exp_gradient.stops == expected_stops
|
||||
|
||||
def test_interpolation_is_batched_without_dropping_gradient_points(self, monkeypatch):
|
||||
renderer = self.renderer(np.float32, 66, 0)
|
||||
renderer._path.projected_points[:, 1] = np.linspace(0, 240, 66, dtype=np.float32)
|
||||
expected_colors, expected_stops = self.reference(renderer)
|
||||
assert len(expected_colors) > 2
|
||||
interp, calls = np.interp, []
|
||||
clip, clip_calls = np.clip, []
|
||||
|
||||
def observed(*args, **kwargs):
|
||||
calls.append(np.asarray(args[0]).size)
|
||||
return interp(*args, **kwargs)
|
||||
|
||||
def observed_clip(*args, **kwargs):
|
||||
clip_calls.append(np.asarray(args[0]).size)
|
||||
return clip(*args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(np, 'interp', observed)
|
||||
monkeypatch.setattr(np, 'clip', observed_clip)
|
||||
renderer._update_experimental_gradient()
|
||||
assert calls == [len(expected_colors), len(expected_colors)]
|
||||
assert clip_calls == [len(expected_colors)]
|
||||
assert [(c.r, c.g, c.b, c.a) for c in renderer._exp_gradient.colors] == expected_colors
|
||||
assert renderer._exp_gradient.stops == expected_stops
|
||||
|
||||
def test_classic_mode_preserves_existing_gradient(self):
|
||||
renderer = self.renderer(np.float32, 66, 0)
|
||||
colors, stops = renderer._exp_gradient.colors, renderer._exp_gradient.stops
|
||||
renderer._experimental_mode = False
|
||||
renderer._update_experimental_gradient()
|
||||
assert renderer._exp_gradient.colors is colors and renderer._exp_gradient.stops is stops
|
||||
|
||||
Reference in New Issue
Block a user