Skip to content

fix(plotting): only pin animation axis ranges when frames exceed the initial view#90

Open
FBumann wants to merge 3 commits into
mainfrom
fix/animation-axis-ranges
Open

fix(plotting): only pin animation axis ranges when frames exceed the initial view#90
FBumann wants to merge 3 commits into
mainfrom
fix/animation-axis-ranges

Conversation

@FBumann

@FBumann FBumann commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Problem

overlay() / add_secondary_y() unconditionally pin explicit numeric ranges on every axis of an animated figure (via _fix_animation_axis_ranges). This caused an empty plot when the user switched an axis to type='category' after combining: the stale numeric range (e.g. [2024, 2041] from year values) gets reinterpreted as category indices, putting the viewport far past the 4 real categories until the autoscale button is pressed.

It also overwrote user-set explicit ranges, and would corrupt log axes (log ranges are exponents, not data values).

Fix

Pin a range only when actually necessary — i.e. when a later frame's data exceeds the extent of fig.data. That is the one case plotly.js autorange can't handle, since it computes the range from the first frame only and never recalculates during animation (the gapminder scenario). Everything else stays on live autorange, which:

  • keeps a post-combine type='category' switch working (frames sharing the same categories never get an x-range pinned),
  • preserves plotly's native autorange padding where possible,
  • respects pre-set explicit ranges and skips log/date/category axes.

Bonus fix: stacked bars were clipped

The pinned y-range was computed from individual segment values, ignoring that barmode='stack'/'relative' stacks them — so tall stacks were clipped at the top (also only visible until pressing autoscale). The range is now computed from per-category positive/negative sums, per frame.

Tests

8 new tests covering: unchanged-extent axes staying on autorange, pinning when a later frame exceeds the initial view, stacked positive/negative sums, explicit-range preservation, log-axis skip, and an end-to-end regression test for the type='category' bug. Existing guarantees (datetime axes untouched, bar zero baseline) still pass. Full suite: 181 passed.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved animation-axis range behavior for overlays, preventing unnecessary pinning when animation frames don’t expand extents.
    • Correctly pins y-range for later frames (with padding) and preserves any explicitly set axis ranges.
    • Enhanced bar-chart handling for stack/relative, including negative stacked totals and keeping the zero baseline.
    • Avoids incorrect range pinning for log-scaled and categorical axes, and prevents stale numeric ranges after axis type switches.
  • Tests
    • Added regression coverage for overlay range pinning, stacked/relative bars, axis-type changes, and user-set range preservation.

…initial view

overlay() and add_secondary_y() unconditionally pinned explicit numeric
ranges on every axis of an animated figure. This broke two cases:

- Switching an axis to type='category' after combining reinterpreted the
  stale numeric range as category indices, showing an empty plot until
  the user pressed the autoscale button.
- User-set explicit ranges and log axes (whose range coordinates are
  exponents) were overwritten with data-coordinate ranges.

Ranges are now pinned only when a later frame's data exceeds the extent
of fig.data — the one case plotly.js autorange cannot handle, since it
computes the range from the first frame only and never recalculates
during animation. Axes already covered by the initial view keep live
autorange, pre-set ranges are respected, and log/date/category axes are
never touched.

Additionally, with barmode='stack'/'relative' the pinned range is now
computed from per-category stacked sums instead of individual segment
values, so tall stacks are no longer clipped at the top.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68337f3f-6a4e-4e28-91dd-881f80154327

📥 Commits

Reviewing files that changed from the base of the PR and between c4c5681 and 342d8bb.

📒 Files selected for processing (2)
  • tests/test_figures.py
  • xarray_plotly/figures.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • xarray_plotly/figures.py
  • tests/test_figures.py

📝 Walkthrough

Walkthrough

Overlay animation-axis range fixing now evaluates frame extents, stacked bars, axis types, and explicit ranges. Tests cover unchanged, expanded, stacked, logarithmic, categorical, and user-defined axis ranges.

Changes

Animation axis pinning

Layer / File(s) Summary
Refactor animation axis range calculation
xarray_plotly/figures.py
Numeric extent extraction, non-linear axis handling, stacked-bar calculations, and conditional range pinning were rewritten; defaultdict is now imported at module scope.
Validate animation range behavior
tests/test_figures.py
Coverage verifies unchanged and expanded frame extents, relative positive and negative stacking, explicit ranges, logarithmic axes, and categorical-axis transitions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Overlay
  participant _fix_animation_axis_ranges
  participant FigureLayout
  Overlay->>_fix_animation_axis_ranges: process base data and animation frames
  _fix_animation_axis_ranges->>FigureLayout: inspect axis types and existing ranges
  _fix_animation_axis_ranges->>FigureLayout: set ranges only for expanded numeric extents
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: animation axis ranges are pinned only when later frames exceed the initial view.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/animation-axis-ranges

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

- Fold the bar zero-baseline into extent collection (a bar's value axis
  simply contributes 0.0), removing the separate zero-clamp pass and
  list-copying.
- Reuse the existing _axis_layout_key() helper instead of reimplementing
  the axis-ref-to-layout-key conversion.
- Drop the float tolerance: frame values are bit-identical to fig.data
  values and sums are computed identically, so exact comparison is safe.
- Inline the two single-use module constants.
- Use deterministic data in the shared-extent test; random values made
  the frame-exceeds-initial condition a coin flip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
xarray_plotly/figures.py (1)

147-202: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Treat mixed-sign barmode='stack' as a separate corner case. The shared positive/negative split matches relative, but stack can diverge with mixed signs, so the computed axis range may be looser than Plotly.js there.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xarray_plotly/figures.py` around lines 147 - 202, Update
_collect_axis_extents so barmode="stack" with mixed-sign bar values is handled
separately from the shared positive/negative accumulation used for relative
stacking. Preserve the existing relative behavior, and ensure stack-mode
candidate extents match Plotly.js for mixed-sign categories rather than
unconditionally adding both positive and negative sums.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@xarray_plotly/figures.py`:
- Around line 147-202: Update _collect_axis_extents so barmode="stack" with
mixed-sign bar values is handled separately from the shared positive/negative
accumulation used for relative stacking. Preserve the existing relative
behavior, and ensure stack-mode candidate extents match Plotly.js for mixed-sign
categories rather than unconditionally adding both positive and negative sums.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7855d423-11eb-4a7d-bca1-527bf268bc8c

📥 Commits

Reviewing files that changed from the base of the PR and between c79ad91 and c4c5681.

📒 Files selected for processing (2)
  • tests/test_figures.py
  • xarray_plotly/figures.py

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant