Tick gutters label the pixels they sit next to - #74
Merged
Merged
Conversation
The 2-D gutters took a value's fraction of the axis array as a fraction of the gutter. imshow axes hold pixel CENTRES, so the first and last centres landed on the gutter's ends and every label stretched outward by up to half a pixel -- the one place left that disagreed with markers, widgets, pointer events and display_to_data. Worse, the gutter spans the whole image area while the image is drawn "contain" inside it, so a pillarboxed or letterboxed image's ticks ran across the empty margin (meshes too). A descending origin='lower' y axis drew no ticks at all. Ticks now go through the transform markers use: axis value -> _axisValToImg2d (the inverse of _imgToAxisVal2d; ascending or descending, continued linearly past the ends; pcolormesh keeps its edge mapping) -> _imgToCanvas2d, which brings the fit rect, zoom < 1, pan clamping and descending axes along. The visible range is the gutter ends through _canvasToImg2d, clamped to the outer pixel edges; label spacing takes abs() so a descending axis keeps its labels. set_view / set_xlim / set_ylim read the axis the same way through Plot2D._axis_extent_frac, the Python mirror of _axisValToImg2d: on imshow the view edges are pixel edges, so set_xlim(-0.5, n - 0.5) is the whole image and set_xlim(0, n - 1) crops the outer half pixels, as matplotlib's imshow does. A descending axis is accepted in either order (set_ylim on an origin='lower' image silently did nothing). The three TestImshowView tests that encoded the stretched mapping now use arange axes and extent values. Claude-Session: https://claude.ai/code/session_01EUrvzeXdzNjPKBTk5jCtp1
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## fix/issues-66-72 #74 +/- ##
====================================================
+ Coverage 91.16% 91.18% +0.02%
====================================================
Files 41 41
Lines 4865 4880 +15
====================================================
+ Hits 4435 4450 +15
Misses 430 430 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #73. Stacked on
fix/issues-66-72, so the diff shows only the tick work. Merge #73 first; GitHub then retargets this PR tomain.What was wrong
The 2-D tick gutters took a value's fraction of the axis array and used it as a fraction of the gutter width. Two things broke:
display_to_dataall use the centre convention, so the ticks were the one part that disagreed.On top of that, an
origin='lower'image (descending y axis) got no y ticks at all, andset_ylimdid nothing on one.Before screenshots are from
fix/issues-66-72, rendered from a git worktree; the tick code there is the same as onmain. After screenshots are from this branch.Fix
_axisValToImg2d→_imgToCanvas2d._axisValToImg2dis the inverse of Pointer events in the pixel-centre convention; bold and outlined text markers #73's_imgToAxisVal2d; it handles ascending or descending arrays and extends linearly past either end. pcolormesh uses_axisValToFrac(arr, v) × n − ½, so its mapping is unchanged. Letterboxing, zoom < 1, pan clamping and descending axes all come from_imgToCanvas2dinstead of a separate calculation. The visible value range is the two gutter ends passed through_canvasToImg2d, clamped to the outer pixel edges. Label spacing usesabs(), so a descending axis also gets its labels.set_view/set_xlim/set_ylimread the axis the same way, through the newPlot2D._axis_extent_frac, which mirrors_axisValToImg2d. They also accept a descending axis.Behaviour change (
api_changefragment)set_xlim(-0.5, n - 0.5)shows the whole image, andset_xlim(0, n - 1)now crops the outer half pixels, as matplotlib'simshowdoes.get_xlim()still returns the first and last axis values. I didn't change it, because other tests treat it as "the axis range"; say if you'd rather it return the extent.TestImshowViewtests encoded the stretched mapping, usinglinspace(0, 32, 32)as if it ran edge to edge. They now usearangeaxes and expect the pixel-edge view described above.Tests
test_plot2d/test_axis_ticks.py:data_to_displayof the pixel centres. Cases: pillarboxed x, letterboxed y, physical units with zoom and pan,origin='lower', and pcolormesh edges.set_viewmath: whole extent, a window between pixel centres, physical units, descending axis, and mesh.fix/issues-66-72, 8 of these fail and the 3 parity tests error because the JS helper doesn't exist there. The two that pass on both are the whole-extent and meshset_viewchecks, whose mapping didn't change.imshow_labels, the gridspec image scenes,pcolormesh_uniform) still pass within tolerance, so I didn't regenerate them from Windows.https://claude.ai/code/session_01EUrvzeXdzNjPKBTk5jCtp1