Pointer events in the pixel-centre convention; bold and outlined text markers - #73
Merged
Merged
Conversation
… markers GH #72: _canvasToImg2d was not the inverse of _imgToCanvas2d. The forward map puts image coordinate i at the centre of pixel i -- the convention marker offsets, widget positions and Python's display_to_data share -- but the inverse skipped the matching -0.5, so every 2-D pointer event read half a pixel right and down of anything drawn at the same spot and round(event.img_x) named pixel i+1 over the right half of pixel i. It now subtracts the 0.5; _imgPix2d / _inImgAxis2d carry the index and bounds rule to the readout, value probe and brush, and _pixelValue2d adds the 0.5 back where texels and the detail tile are addressed by edge. The wheel-zoom anchor compensates so the point under the cursor still stays put, and brush strokes now land under the cursor. xdata/ydata go through _imgToAxisVal2d: imshow axes hold one value per pixel centre, so pixel i reads exactly x_axis[i] (continued linearly over the outer half of the edge pixels rather than clamped); pcolormesh axes are cell edges and keep the (i + 0.5) / n mapping, i.e. the values they already reported. GH #66: add_texts / add_text take fontweight ('normal' | 'bold' | CSS number) and outline_color / outline_width, a halo stroked under the fill so a label stays legible on light and dark ground. Validated on creation and on MarkerGroup.set; the 1-D and 2-D renderers share _markerTextStyle / _drawMarkerText. FIGURE_ESM.md anchors are set to the current line numbers (they were one line off throughout on main). 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 @@
## main #73 +/- ##
==========================================
+ Coverage 91.12% 91.16% +0.03%
==========================================
Files 41 41
Lines 4847 4865 +18
==========================================
+ Hits 4417 4435 +18
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.
Closes #72. Closes #66.
#72 — pointer coordinates are half a pixel off
The same physical click (cyan cross) recorded from the
mainrenderer and from this branch. The red square is the pixelround(event.img_x), round(event.img_y)picks._imgToCanvas2dputs image coordinate i at the centre of pixel i, the same convention marker offsets, widget positions and Python'sdisplay_to_datause._canvasToImg2dskipped the matching-0.5, so the two weren't inverses. Every 2-D pointer event read half a pixel right and down of anything drawn at the same spot._canvasToImg2dnow subtracts the0.5. The round trip is exact at every zoom and pan (it was off by0.5before)._imgPix2d(floor(v + 0.5)) gives the pixel index and_inImgAxis2dchecks bounds. The readout, the value probe, the brush bounds and_inImage2dall use them._pixelValue2dadds the0.5back itself, because it addresses texels and the detail tile by pixel edge.xdata/ydataforpointer_down,double_click,pointer_settled,key_downand the readout now come from_imgToAxisVal2d:x_axis[i]. Past the first or last pixel centre the value extends linearly instead of clamping.(i + 0.5) / n, which gives exactly the values it reported before, so meshxdatadoesn't move.0.5back, so zooming still keeps the image point under the cursor fixed.This is a behaviour change, so it has an
api_changefragment. Handlers should useround(event.img_x)to get a pixel index.int()no longer does that, anddocs/events.rstused it, so the docs are updated. The bundled examples already usedround().Not changed: the imshow tick gutters still stretch
x_axis[0]…x_axis[-1]from the image's left edge to its right edge, not from the first pixel centre to the last. A tick label can therefore sit up to half a pixel from thexdataof a click at the same spot. That mapping was already inconsistent with marker placement. Fixing it moves tick positions, so it is a separate stacked PR: #74. That PR also turned up a letterbox bug in the gutters and the missingorigin='lower'y ticks.#66 — bold and outlined text markers
add_texts(Plot2D and Plot1D) andadd_textgain three options:fontweight:"normal"(default),"bold", or a CSS numeric weight in[1, 1000]. The name matchesfontsize, following matplotlib.outline_color: a halo colour,Noneby default. The halo is drawn withstrokeTextunderfillText, with round joins.outline_width: default3.0. Roughly half of it shows outside the glyphs.The options are validated on creation and on
MarkerGroup.set, so a bad value is refused before it reaches the group. The 1-D and 2-D renderers share_markerTextStyle/_drawMarkerText. Plot2D'sadd_textsalso gets the docstring it was missing.Tests
test_plot2d/test_pointer_pixel_centre.py: round trip of the extracted JS helpers across zoom and pan, the imshow and mesh axis mapping, and real Playwright clicks placed viadata_to_display. The three click tests fail onmainand pass here.test_markers/test_text_style.py: wire format, validation, liveset, and screenshot checks on 1-D and 2-D panels. Bold puts down more ink; the outline paints its own colour while the fill stays on top.FIGURE_ESM.mdanchors are updated to the current line numbers. They were uniformly one line off onmain.https://claude.ai/code/session_01EUrvzeXdzNjPKBTk5jCtp1