Skip to content

Commit e511863

Browse files
committed
optimize _map_line_to_polygon
1 parent dad7bb5 commit e511863

File tree

2 files changed

+34
-55
lines changed

2 files changed

+34
-55
lines changed

selfdrive/ui/onroad/model_renderer.py

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -354,60 +354,58 @@ def _map_line_to_polygon(self, line: np.ndarray, y_off: float, z_off: float, max
354354
if points.shape[0] == 0:
355355
return np.empty((0, 2), dtype=np.float32)
356356

357-
N = points.shape[0]
358-
# Generate left and right 3D points in one array using broadcasting
359-
offsets = np.array([[0, -y_off, z_off], [0, y_off, z_off]], dtype=np.float32)
360-
points_3d = points[None, :, :] + offsets[:, None, :] # Shape: 2xNx3
361-
points_3d = points_3d.reshape(2 * N, 3) # Shape: (2*N)x3
357+
lr_points = np.stack((points, points), axis=1).astype(np.float32)
358+
lr_points[:, 0, 1] -= y_off
359+
lr_points[:, 1, 1] += y_off
360+
lr_points[:, :, 2] += z_off
362361

363362
# Transform all points to projected space in one operation
364-
proj = self._car_space_transform @ points_3d.T # Shape: 3x(2*N)
365-
proj = proj.reshape(3, 2, N)
366-
left_proj = proj[:, 0, :]
367-
right_proj = proj[:, 1, :]
363+
proj = (self._car_space_transform @ lr_points.reshape(-1, 3).T).T.reshape(-1, 2, 3)
368364

369365
# Filter points where z is sufficiently large
370-
valid_proj = (np.abs(left_proj[2]) >= 1e-6) & (np.abs(right_proj[2]) >= 1e-6)
371-
if not np.any(valid_proj):
366+
z_vals = np.abs(proj[:, :, 2])
367+
valid_pairs = (z_vals >= 1e-6).all(axis=1)
368+
if not valid_pairs.any():
372369
return np.empty((0, 2), dtype=np.float32)
373370

374-
# Compute screen coordinates
375-
left_screen = left_proj[:2, valid_proj] / left_proj[2, valid_proj][None, :]
376-
right_screen = right_proj[:2, valid_proj] / right_proj[2, valid_proj][None, :]
371+
proj = proj[valid_pairs]
372+
373+
screen = proj[:, :, :2] / proj[:, :, 2:3]
374+
left_screen = screen[:, 0, :]
375+
right_screen = screen[:, 1, :]
377376

378377
# Define clip region bounds
379378
clip = self._clip_region
380379
x_min, x_max = clip.x, clip.x + clip.width
381380
y_min, y_max = clip.y, clip.y + clip.height
382381

383382
# Filter points within clip region
384-
left_in_clip = (
385-
(left_screen[0] >= x_min) & (left_screen[0] <= x_max) &
386-
(left_screen[1] >= y_min) & (left_screen[1] <= y_max)
387-
)
388-
right_in_clip = (
389-
(right_screen[0] >= x_min) & (right_screen[0] <= x_max) &
390-
(right_screen[1] >= y_min) & (right_screen[1] <= y_max)
383+
in_clip = (
384+
(left_screen[:, 0] >= x_min) & (left_screen[:, 0] <= x_max) &
385+
(left_screen[:, 1] >= y_min) & (left_screen[:, 1] <= y_max) &
386+
(right_screen[:, 0] >= x_min) & (right_screen[:, 0] <= x_max) &
387+
(right_screen[:, 1] >= y_min) & (right_screen[:, 1] <= y_max)
391388
)
392-
both_in_clip = left_in_clip & right_in_clip
393-
394-
if not np.any(both_in_clip):
389+
if not in_clip.any():
395390
return np.empty((0, 2), dtype=np.float32)
396391

397-
# Select valid and clipped points
398-
left_screen = left_screen[:, both_in_clip]
399-
right_screen = right_screen[:, both_in_clip]
392+
left_screen = left_screen[in_clip]
393+
right_screen = right_screen[in_clip]
400394

401-
# Handle Y-coordinate inversion on hills
402-
if not allow_invert and left_screen.shape[1] > 1:
403-
y = left_screen[1, :] # y-coordinates
404-
keep = y == np.minimum.accumulate(y)
405-
if not np.any(keep):
395+
if not allow_invert and left_screen.shape[0] > 1:
396+
y_vals = left_screen[:, 1]
397+
keep = y_vals == np.minimum.accumulate(y_vals)
398+
if not keep.any():
406399
return np.empty((0, 2), dtype=np.float32)
407-
left_screen = left_screen[:, keep]
408-
right_screen = right_screen[:, keep]
400+
left_screen = left_screen[keep]
401+
right_screen = right_screen[keep]
402+
403+
k = left_screen.shape[0]
404+
tri_strip = np.empty((k * 2, 2), dtype=np.float32)
405+
tri_strip[0::2] = left_screen
406+
tri_strip[1::2] = right_screen
409407

410-
return np.vstack((left_screen.T, right_screen[:, ::-1].T)).astype(np.float32)
408+
return tri_strip
411409

412410
@staticmethod
413411
def _hsla_to_color(h, s, l, a):

system/ui/lib/shader_polygon.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -195,22 +195,6 @@ def _configure_shader_color(state: ShaderState, color: Optional[rl.Color], # no
195195
state.fill_color_ptr[0:4] = [color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0]
196196
rl.set_shader_value(state.shader, state.locations['fillColor'], state.fill_color_ptr, UNIFORM_VEC4)
197197

198-
199-
def triangulate(pts: np.ndarray) -> list[tuple[float, float]]:
200-
"""Only supports simple polygons with two chains (ribbon)."""
201-
202-
# TODO: consider deduping close screenspace points
203-
# interleave points to produce a triangle strip
204-
assert len(pts) % 2 == 0, "Interleaving expects even number of points"
205-
206-
tri_strip = []
207-
for i in range(len(pts) // 2):
208-
tri_strip.append(pts[i])
209-
tri_strip.append(pts[-i - 1])
210-
211-
return cast(list, np.array(tri_strip).tolist())
212-
213-
214198
def draw_polygon(origin_rect: rl.Rectangle, points: np.ndarray,
215199
color: Optional[rl.Color] = None, gradient: Gradient | None = None): # noqa: UP045
216200

@@ -232,12 +216,9 @@ def draw_polygon(origin_rect: rl.Rectangle, points: np.ndarray,
232216
# Configure gradient shader
233217
_configure_shader_color(state, color, gradient, origin_rect)
234218

235-
# Triangulate via interleaving
236-
tri_strip = triangulate(pts)
237-
238219
# Draw strip, color here doesn't matter
239220
rl.begin_shader_mode(state.shader)
240-
rl.draw_triangle_strip(tri_strip, len(tri_strip), rl.WHITE)
221+
rl.draw_triangle_strip(points.tolist(), len(points), rl.WHITE)
241222
rl.end_shader_mode()
242223

243224

0 commit comments

Comments
 (0)