Skip to contents

Ranks a scene's keyed elements by their distance to a point — measuring to the actual geometry, not to a bounding box.

Usage

vl_nearest(scene, x, y, units = c("npc", "px"), n = 1L, max_dist = Inf)

Arguments

scene

A vl_scene() (or anything with an as_vellum_scene() method).

x, y

The probe point.

units

"npc" (default, y up) or "px" (device pixels, y down) — the same convention as hit_test().

n

How many nearest elements to return.

max_dist

Ignore anything further than this many device pixels.

Value

A data frame of key, kind and dist (device px), nearest first. Zero rows if nothing is in range. Only keyed elements are considered — an unkeyed mark is not addressable, so reporting it would be no use.

Why not the bounding box

For a round or upright mark the two agree. For anything diagonal or thin they do not, and the difference is not subtle: a line from the bottom-left of a panel to the top-right has a bounding box covering the entire panel, so a box-based "what is nearest" reports it from anywhere in the plot. That is why a host holding only boxes has to exclude such marks from hover rather than rank them wrongly.

Distances are: to the disc for round marks (zero inside), to the rectangle for rects (zero inside), perpendicular to the segment for segments, to the nearest edge for open polylines, and zero anywhere inside a closed polygon or path — so clicking the middle of a choropleth region hits that region.

Marks vellum cannot describe more precisely than a box — labels, rounded rects — fall back to their box, which for a label is a fair description of the target anyway.

For a client-side host

A browser cannot call back into R on every mouse move. Use element_geometry() instead: it returns the same true geometry once, and the client computes distances locally. vl_nearest() is for hosts that can ask — a Shiny app, a test, a script.

See also

element_geometry() for the geometry itself, scene_model() for bounding boxes, hit_test() for "which grob is under this pixel".

Examples

s <- vl_scene(4, 3, dpi = 96, bg = "white") |>
  draw(segments_grob(0.1, 0.1, 0.9, 0.9, key = "diagonal")) |>
  draw(points_grob(0.8, 0.2, key = "corner"))

# Near the corner the diagonal's *bounding box* also contains this point,
# but its geometry is far away.
vl_nearest(s, 0.8, 0.2)
#>      key  kind dist
#> 2 corner point    0