Skip to content

Comments

dataless brush#2360

Open
Fil wants to merge 37 commits intofil/pixel-roundfrom
fil/brush-dataless
Open

dataless brush#2360
Fil wants to merge 37 commits intofil/pixel-roundfrom
fil/brush-dataless

Conversation

@Fil
Copy link
Contributor

@Fil Fil commented Feb 13, 2026

A brush mark that renders a 2D rectangular brush, enabling selection and filtering. The brush mark has aria-label="brush". The mark does not take data; instead, it offers a mechanism to filter other marks while brushing, as well as any dataset.

The brush dispatches input events with the selection bounds {x1, x2, y1, y2} in data space and a filter function to test whether a point falls within the selection.

Brushes support faceted plots: each facet gets its own brush, starting one clears the others, and the value includes fx/fy as relevant (with support in the filter function).

Three reactive mark options transforms — brush.inactive, brush.context, brush.focus — allow marks to respond to state by showing/hiding points inside or outside the selection. Reactive marks re-render as the brush moves, with pointerEvents set to "none" by default so they don't obstruct interaction if they are above the brush (which achieves better contrast in general).

The brush supports geographic projections: in that case, though, the {x1, x2, y1, y2} coordinates are returned in pixel space; but the filter projects lon/lat automatically for hit-testing.

A pending flag distinguishes brushing events (user gesture) from committed selections when the brush gesture ends and the user releases the pointer. This is to allow workflows where we don't want to rerender continuously during brushing (e.g. to avoid costly calls to a database backend).

The brush has a move property that allows the selection to be modified programmatically.

Data-less 1D brushes (brushX, brushY) are supported too, with interval snapping (#2364); their aria-label is brush-x/brush-y.

Alternatives considered:

  • Instead of brush.move we could imagine supporting a plot.value = {} setter. I've investigated this, and the code I came up with felt brittle. Furthermore, it seems more discoverable to use brush.move, and for advanced applications people will want to create the brush outside of Plot anyway. Another thing that this makes unclear, is what happens when the brush mark is reused. (But we can still add this later if necessary.)
  • a .done instead of the .pending flag (with opposite logic); it feels equivalent, no preference.

Additional PRs:

(I've made these as separate PRs to help review… but maybe it's fine to review the final boss #2370)

Ideas for related future PRs:

AI disclosure: I used @claude to help me generate the unit tests and maintain an action plan.

closes #5
supersedes #71
supersedes #1653
needs #2038

Fil added 17 commits February 12, 2026 17:55
this might be a bit controversial, but it feels much easier to do the right thing by default than to have to teach it
rationale: I thought it would help a bit with the problem of the RHS points not being selectable when they define the extent. However, the points on the rhs of the domain will still be brushable since we added a small padding to the brush extent.
@Fil Fil requested a review from mbostock February 13, 2026 14:43
@Fil Fil mentioned this pull request Feb 20, 2026
Fil added 7 commits February 20, 2026 11:55
Since we want to support brushing histograms, we needed two additional features:
* an **interval** option for snapping the brush on gesture end.
* support for X1/X2 channels in renderFilter, for rect marks.

(This will require some work to merge with #2363)
# Conflicts:
#	src/interactions/brush.js
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Fil Fil mentioned this pull request Feb 20, 2026
Fil added 7 commits February 20, 2026 18:26
to avoid the context line connecting through the brush, we split the data into several runs, broken when a point is filtered out. (typically on a line chart, this makes one line on the lhs of the brush, the other on the rhs)
this inset is making it possible to select values that are ON the edge, but it's nicer to the eye to not have it where it's not necessary
@Fil Fil marked this pull request as ready for review February 24, 2026 14:54
@Fil Fil changed the base branch from main to fil/pixel-round February 24, 2026 15:01
@Fil Fil requested a review from allisonhorst February 24, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Brushing

1 participant