Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ in the README).
stay uniform.

### Changed
- All Polar charts with the exception of wind rose disable zoom by default.
- `default_drag_action` accepts only `"auto"`/`"none"` on a polar chart; the
values naming drag tools a disc does not have now raise at construction
instead of being accepted and silently doing nothing.
- Default tooltips now lead with the hovered series name, and the radial row of
a polar readout is labelled `r` rather than presented as a Cartesian `y`. The
numeric angle row is gone from polar readouts: on most polar charts the angle
Expand Down
11 changes: 7 additions & 4 deletions docs/api-reference/limitations-and-alpha-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,13 @@ and [Benchmarks](/docs/xy/overview/benchmarks/) for scoped evidence.
- Linked views synchronize viewport axes, not selections or cross-filtering.
- Facets support display/export and shared domains but not `Chart` append,
pick, or Python-side range-selection methods.
- Polar interaction currently consists of hover, radial zoom about a fixed
radial minimum, and reset. Authored sectors are supported; theta pan/rotation,
interactive sector zoom, box zoom, selection, brushing, and crosshairs are
disabled.
- Polar interaction currently consists of hover plus opt-in radial zoom about a
fixed radial minimum. Zoom defaults to off (`xy.wind_rose()` excepted) and is
enabled per chart with `xy.interaction_config(zoom=True)` or a `zoom=True` chart
prop; reset is hidden unless zoom or an explicit `reset_axes` gives it axes to
restore. `default_drag_action` accepts only `"auto"`/`"none"` on a polar chart.
Authored sectors are supported; theta pan/rotation, interactive sector zoom, box
zoom, selection, brushing, and crosshairs are disabled.
- Point-anchored `text`, `label`, `marker`, `arrow`, and `callout` annotations
use the joint `(theta, r)` projection consistently on polar charts in the
browser, SVG, and native raster output. Polar rules and bands remain deferred
Expand Down
22 changes: 21 additions & 1 deletion docs/app/tests/test_docs_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -1603,7 +1603,19 @@ def test_polar_guides_track_the_current_coordinate_system_contract() -> None:
)

for fragment in (
"radial maximum while keeping the radial",
"scales the radial maximum",
"keeps the radial minimum fixed",
# Zoom is off by default on polar (polar-axes.md §8), and the guide has
# to carry both halves of that: why, and the exact way back in. Reset
# follows the zoom capability, so the page must not promise it either.
"**Zoom is off by default.**",
"reset-axis policy is empty",
# Reset is not unconditionally absent: an authored `reset_axes` grants it
# with zoom still off, and `default_drag_action` is narrowed under polar.
"An explicit `reset_axes` also grants reset",
'accepts only `"auto"` and `"none"` here',
"xy.interaction_config(zoom=True)",
"def zoomable_polar_demo():",
"authored fractional degree",
"splits a line into visible runs",
"`line`, `scatter`, and `area` are limited to",
Expand Down Expand Up @@ -1709,6 +1721,14 @@ def test_polar_guides_track_the_current_coordinate_system_contract() -> None:
"Partial-sector layouts are not implemented",
"validated log-r semantics",
"Each polar trace is limited to 200,000 points",
# Zoom, and the reset that follows it, are opt-in on polar (polar-axes.md
# §8). No polar guide may promise either as part of the default contract,
# and none may claim a non-wind-rose radius is merely layout — an ordinary
# `polar_chart` carrying measured radii is the documented opt-in case.
"Wheel and modebar zoom scale the radial maximum",
"radial wheel zoom, double-click reset",
"hover, radial-only zoom",
"its radius is layout rather than data",
):
assert stale_fragment not in polar_guides
assert stale_fragment not in matplotlib
Expand Down
20 changes: 16 additions & 4 deletions docs/charts/pie-chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -674,10 +674,22 @@ bar mark per category.

Pie blocks use the shared polar renderer. These examples set
`xy.modebar(show=False)` to keep the presentation clean. The underlying
interactions and APIs remain intact: sector hover, radial wheel zoom,
double-click reset, and browser/static exports remain available. A hovered slice
reads its own label and value; the layout angle and the constant rim radius stay
out of the readout.
interactions and APIs remain intact: sector hover works, and
browser/static exports remain available. A hovered slice reads its own label and
value; the layout angle and the constant rim radius stay out of the readout.

Zoom is off by default here, as on every polar chart except the wind rose. A pie
carries its value in the *angle* and uses the radius as a constant rim, so
zooming would crop that rim around a fixed center rather than reveal anything —
and leaving the wheel free means a page scrolls normally over the chart. With
nothing to move the view, there is nothing to restore either: unless `reset_axes`
is authored, double-click reset does nothing and the reset controls drop out of the
modebar. An authored `reset_axes` grants both back on its own, whatever the zoom
switch says. Add `xy.interaction_config(zoom=True)` to restore the wheel and
double-click reset; these examples also hide the modebar with
`xy.modebar(show=False)`, so drop that child too if you want the visible zoom and
reset buttons. See
[why zoom is off by default](/docs/xy/charts/polar-chart/#why-zoom-is-off-by-default).
Center labels and legends composed in Reflex are browser UI; annotations placed
with `xy.text()` are part of the chart and are preserved in SVG and native
raster exports.
Expand Down
85 changes: 81 additions & 4 deletions docs/charts/polar-chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,13 +253,90 @@ Cartesian charts:
already on it — while an authored spoke label survives and
`xy.tooltip(labels={"x": ...})` opts the angle back in. A hole or excluded
part of a sector is not hit-testable.
- Wheel and modebar zoom scale the radial maximum while keeping the radial
minimum fixed. Reset restores the original radial range.
- **Zoom is off by default.** Polar charts ship without wheel zoom, modebar zoom
controls, or the zoom percentage indicator. Reset follows by default: with
nothing to move the view, the derived reset-axis policy is empty, so Fit Data
and Reset View are absent and double-click has nothing to restore. Wind roses
are the exception and keep zoom on. Enabling zoom brings the wheel, the whole
modebar zoom menu, and double-click reset back: zoom scales the radial maximum
while keeping the radial minimum fixed, and reset restores the original radial
range. An explicit `reset_axes` also grants reset on its own — controls and
double-click alike, whatever the zoom switch says — which is what a chart whose
view moves through linked axes or state-driven updates rather than a gesture
should use. Its modebar trigger shows a view-controls icon rather than a zoom
percentage, because with zoom off nothing can move that number.
- Drag does nothing on a disc, and says so: theta pan, box zoom, and
rectangular/lasso selection have no polar geometry, so `default_drag_action`
accepts only `"auto"` and `"none"` here and raises on the rest rather than
resolving to a tool that cannot engage.
- Theta rotation/panning, interactive sector zoom, box zoom, rectangular or lasso
selection, brushing, and crosshairs are not currently available.

Keeping the radial minimum fixed prevents an ordinary zoom from unexpectedly
turning a disc into an annulus. See
### Why Zoom Is Off by Default

The center of a polar chart is a fixed point: zoom scales the radial maximum
while holding the radial minimum in place. That is well behaved when the radius
is a measured quantity, and misleading when it is not. A pie or donut carries its
value in the *angle* and uses the radius as a constant rim; a radial bar chart,
gauge, or radar sits on a fixed frame. Zooming those crops the rim while the
geometry stays welded to the middle of the disc, which reads as a broken chart
rather than as navigation.

`xy.wind_rose()` keeps zoom enabled because its radius genuinely is data — a
frequency count per direction — so pulling the outer ring in magnifies the short
sectors of a rose dominated by one prevailing direction.

Leaving zoom off also means the chart does not capture the wheel, so a page
scrolls normally when the cursor passes over a pie or gauge.

### Enable Zoom on a Polar Chart

Add an `xy.interaction_config(zoom=True)` child when the radius is a measured
quantity worth magnifying. Because the radial minimum stays pinned, zooming in
enlarges the values nearest the center — which is what you want when a single
large lobe squashes the rest of the pattern against the middle of the disc:

~~~python demo exec
import numpy as np
import reflex_xy
import xy

bearing = np.linspace(0.0, 360.0, 721)
offset = (bearing + 180.0) % 360.0 - 180.0
# Linear radiated power: one dominant main lobe plus side lobes a twentieth its
# size, so the side-lobe structure only becomes readable once the radial axis is
# zoomed in.
power = np.exp(-(offset / 16.0) ** 2) + 0.05 * np.abs(np.cos(np.radians(3.0 * bearing)))

zoomable_polar = xy.polar_chart(
xy.line(bearing, power, color="#6e56cf", width=2.0),
xy.theta_axis(unit="degrees", zero="N", direction="clockwise"),
xy.r_axis(label="radiated power"),
xy.interaction_config(zoom=True),
title="Antenna pattern — scroll to magnify the side lobes",
)


def zoomable_polar_demo():
return reflex_xy.chart(zoomable_polar, height="420px")
~~~

The same flag is available directly on the chart for one-off cases
(`xy.polar_chart(..., zoom=True)`), and `xy.interaction_config(zoom=False)` turns
zoom off on a wind rose. Related switches narrow the gesture further once zoom is
enabled: `wheel_zoom=False` keeps the modebar controls but releases the wheel,
`zoom_buttons=False` does the reverse, and `zoom_limits=(1.0, 8.0)` caps how far
in the radius can go. `box_zoom`, `select`, `brush`, and `crosshair` stay off on
polar charts whatever their flags say — those gestures are rectangles and have no
polar geometry yet.

Radial zoom always keeps the radial minimum fixed, so an ordinary zoom never
turns a disc into an annulus; author a deliberate annulus with `r_axis(hole=...)`
or `r_axis(origin=...)` instead. One consequence is worth knowing before you
enable it: values above the zoomed radial maximum are culled rather than clamped
for lines and points, so zooming in on a chart whose interesting structure sits at
the rim hides it instead of enlarging it. Reach for `r_axis(type_="log")` when a
wide radial range needs to be readable at every scale at once. See
[Interactions and selections](/docs/xy/core-concepts/interactions/) for the
general interaction configuration surface.

Expand Down
15 changes: 11 additions & 4 deletions docs/charts/radar-chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,17 @@ def radar_scaled_demo():

## Interaction and Export

Radar charts use the shared polar interaction model: hover, radial-only zoom,
and reset are available; rotation, box zoom, selection, brushing, and
crosshairs are disabled. Browser, SVG, PDF, and native raster exports share the
same radar geometry.
Radar charts use the shared polar interaction model: hover is available; zoom is
off by default, and rotation, box zoom, selection, brushing, and crosshairs are
disabled. Browser, SVG, PDF, and native raster exports share the same radar
geometry.

A radar profile is read against a fixed frame of spokes, and zoom scales the
outer ring about a fixed center, so it crops that frame instead of magnifying the
comparison. Add `xy.interaction_config(zoom=True)` (or `zoom=True` on the chart)
to enable radial-only zoom when the shared scale is worth magnifying; reset comes
with it, since only then does a gesture exist to undo. An explicit `reset_axes`
grants reset by itself for a chart driven from application state.

See [Polar chart interaction and limits](/docs/xy/charts/polar-chart/#hover-and-zoom)
for the complete coordinate-system contract.
Expand Down
14 changes: 12 additions & 2 deletions docs/charts/radial-bar-chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -682,8 +682,18 @@ own bounding box.
## Interaction and Export

All live blocks on this page set `xy.modebar(show=False)` to keep the
presentation quiet. Sector hover, radial wheel zoom, double-click reset, and
browser/static exports remain available through the shared polar renderer.
presentation quiet. Sector hover and browser/static exports remain available
through the shared polar renderer.

Zoom is off by default, as on every polar chart except the wind rose: these
compositions are laid out against a fixed center and a fixed outer ring, so
zooming crops the sectors rather than revealing detail. With no gesture able to
move the view, reset has nothing to restore either: unless `reset_axes` is
authored, double-click is inert and the reset controls drop out of the modebar. An
authored `reset_axes` grants both back on its own, whatever the zoom switch says.
Add `xy.interaction_config(zoom=True)` to turn radial zoom (and reset) on when the
sector lengths are worth magnifying — see
[why zoom is off by default](/docs/xy/charts/polar-chart/#why-zoom-is-off-by-default).

Reflex-composed center values, rails, statistics, and custom legends are browser
UI. The radial sectors themselves remain part of the XY chart and are preserved
Expand Down
14 changes: 14 additions & 0 deletions docs/charts/wind-rose.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,20 @@ Wind roses support hover, fixed-minimum radial zoom, reset, and browser/static
export through the shared polar renderer. Theta rotation, box zoom, selection,
brushing, and crosshairs are not available.

A wind rose is the one polar chart that keeps zoom **on** by default. Its radius
is a frequency count, so pulling the outer ring in magnifies the short sectors of
a rose dominated by one prevailing direction; the radial minimum stays pinned at
zero, so the disc never becomes an annulus.

Every other polar chart type defaults to zoom off — not because its radius can
never be data, but because a fixed center means radial zoom crops the rim rather
than navigating the chart, and that is the wrong default for the compositions
built on a constant rim or a fixed frame. A `polar_chart()` carrying measured
radial values is exactly the case to
[opt back in](/docs/xy/charts/polar-chart/#enable-zoom-on-a-polar-chart). Pass
`xy.interaction_config(zoom=False)` to opt a rose out — for instance when it is
embedded in a scrolling page and should not capture the wheel.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

See the [polar overview](/docs/xy/charts/polar-chart/) for the full interaction,
renderer, annotation, and large-data boundary.

Expand Down
25 changes: 18 additions & 7 deletions docs/components/modebars-and-interaction-controls.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,24 @@ Back and Next view-history controls live at the top of the zoom menu. They
disable automatically at the ends of the history, remain open while stepping
through views, and a new navigation after going Back clears the forward stack.

Polar charts use a narrower control contract: hover, radial wheel/button zoom,
reset, and export remain available. Radial zoom scales `r_max` about a fixed
`r_min` while preserving an authored `hole` or `origin`. Authored angular
sectors render normally, but Pan/theta rotation, interactive sector zoom, box
zoom, selection, brushing, and crosshairs do not yet have polar semantics and
stay disabled even when a general interaction flag is enabled. The polar
modebar therefore omits the Pan button.
Polar charts use a narrower control contract: hover and export remain available,
and zoom is off by default — `xy.wind_rose()` is the one polar chart that ships
with it on. By default a polar chart without zoom shows no zoom menu at all: no
zoom percentage, no Zoom In/Out, no Fit Data or Reset View, and no Back/Next
history, because no local viewport control is enabled. An explicit `reset_axes`
still exposes Fit Data and Reset View on their own. When reset, history, or
another view control keeps this menu available while `zoom=False`, its trigger
shows a view-controls icon instead of a zoom percentage because local zoom
cannot change that number. Add
`xy.interaction_config(zoom=True)` and the whole menu returns, including radial
wheel/button zoom, which scales `r_max` about
a fixed `r_min` while preserving an authored `hole` or `origin`. Authored angular sectors render
normally, but Pan/theta rotation, interactive sector zoom, box zoom, selection,
brushing, and crosshairs do not yet have polar semantics and stay disabled even
when a general interaction flag is enabled. The polar modebar therefore omits the
Pan button, and `default_drag_action` accepts only `"auto"` or `"none"` on a polar
chart — the other values name drag tools a disc does not have, so they raise
instead of resolving to nothing.

## The default toolbar

Expand Down
42 changes: 35 additions & 7 deletions docs/core-concepts/interactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,41 @@ is no separate transport configuration flag.

### Polar interaction boundary

Polar charts keep hover, radial wheel/modebar zoom, and reset. Radial zoom
changes the maximum radius while holding the minimum fixed, so zooming a disc
does not unexpectedly create a hole and an authored `hole` or `origin` remains
stable. Authored sectors are supported, but theta rotation/panning, interactive
sector zoom, box zoom, selection, brushing, and crosshairs are disabled until
those gestures have polar-native geometry. Polar charts do not expose the Pan
button, and interaction flags cannot opt one into an unsupported gesture.
Polar charts keep hover. Zoom is the one default that differs from Cartesian
charts: `zoom` resolves to `False` under `coords="polar"`. The center of a disc is
a fixed point of the polar transform and radial zoom pins the minimum, so zooming
in crops the outer ring rather than magnifying the chart around the cursor. That
is the wrong default for a composition read against an authored frame — a pie or
donut, whose value is the angle and whose radius is a constant rim, but also a
radial bar, gauge, or radar, where the radial extent *is* the value yet is read
against a fixed rim or shared domain that zoom would clip away.
`xy.wind_rose()` is the exception and ships with zoom on, since its radius is a
frequency count and pulling the ring in magnifies the short sectors.

Opt back in per chart with `xy.interaction_config(zoom=True)` (or `zoom=True` on
the chart) — an ordinary `polar_chart()` whose radius is measured data is the
expected case for this. Radial zoom then changes the maximum radius while holding
the minimum fixed, so zooming a disc does not unexpectedly create a hole and an
authored `hole` or `origin` remains stable.

While zoom is off, no gesture can move the view, so the *derived* reset-axis
policy is empty and reset is not part of the default polar contract either: with
no `reset_axes` authored, the modebar shows no zoom or reset controls and
double-click has nothing to restore. Zoom brings both back. An authored
`reset_axes` is honored regardless of the zoom switch, and grants reset — controls
and double-click alike — on its own; that is the right choice for a chart whose
view moves through linked axes or application state rather than a gesture. The
chart also leaves wheel events uncancelled while zoom is off, so the surrounding
page keeps scrolling under the cursor.

`default_drag_action` accepts only `"auto"` or `"none"` on a polar chart. The
other values name drag tools a disc does not have, so they raise at construction
instead of resolving to no tool at all.

Authored sectors are supported, but theta rotation/panning, interactive sector
zoom, box zoom, selection, brushing, and crosshairs are disabled until those
gestures have polar-native geometry. Polar charts do not expose the Pan button,
and interaction flags cannot opt one into an unsupported gesture.

See the [polar chart guide](/docs/xy/charts/polar-chart/#hover-and-zoom) for the
complete coordinate-system boundary.
Expand Down
3 changes: 2 additions & 1 deletion docs/guides/display-and-export.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ chart.to_html("chart.html")
HTML export is self-contained: it includes the chart spec, binary data, and
bundled render client. It keeps the interactions supported by that chart and
coordinate system—zoom, pan, hover, and selection on ordinary Cartesian
charts, with the narrower radial-zoom/hover/reset contract on polar charts—and
charts, with the narrower hover contract on polar charts, whose radial zoom is
opt-in except on a wind rose—and
built-in chart chrome. It does not need a browser or network connection at
export time.

Expand Down
Loading
Loading