CODAP-1526: draw residual points with the category's shape - #2699
CODAP-1526: draw residual points with the category's shape#2699kswenson wants to merge 1 commit into
Conversation
|
Reviewing this stack — seven PRs, each based on the one below it, merging bottom-up into
CODAP-1506 is split across #2691 and #2693, so approving #2691 does not complete that story — it should not leave code review until #2693 is approved too. The other five map one-to-one. Nothing here is superseded — this is the top of the stack, and the last of the four surfaces that draw a point. Worth your attention:
This completes the set: canvas (#2696), PIXI (#2697), legend keys (#2698), residual plot (#2699). Nothing else in the codebase draws a per-case mark. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## CODAP-1507-legend-key-shapes #2699 +/- ##
================================================================
- Coverage 87.94% 87.93% -0.02%
================================================================
Files 821 821
Lines 47566 47567 +1
Branches 12142 12143 +1
================================================================
- Hits 41834 41826 -8
- Misses 5716 5725 +9
Partials 16 16
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
codap-v3
|
||||||||||||||||||||||||||||
| Project |
codap-v3
|
| Branch Review |
CODAP-1526-residual-plot-shapes
|
| Run status |
|
| Run duration | 09m 04s |
| Commit |
|
| Committer | null |
| View all properties for this run ↗︎ | |
| Test results | |
|---|---|
|
|
0
|
|
|
2
|
|
|
82
|
|
|
0
|
|
|
384
|
| View all changes introduced in this branch ↗︎ | |
There was a problem hiding this comment.
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
6c717ef to
c3eec55
Compare
03a3ae8 to
fd5e6a3
Compare
c3eec55 to
e6ae88f
Compare
The residual plot draws its own SVG rather than going through a point renderer, so its points stayed circles whatever the upper plot was drawn with. It is the fourth and last surface that draws a point, after the two renderers and the legend key. A point is an outline now, positioned by a translate rather than a center. The radius moves to a data attribute because an outline has none to read back, and everything that decides how a point is drawn reads it from there. Growing a new point regenerates its outline at each step rather than scaling it. A scale takes the stroke with it, and the stroke is what marks a point selected, so a selected point would have faded in with an outline that thickened as it grew. Regenerating leaves the whole of the existing fade-in alone, including the guard against redirecting a transition that is already running. The shape comes from the same lookup the upper plot uses, so the two agree. Corrects the claim that a residual plot never has a legend. It is turned off for a categorical legend only when the line is an LSRL, so a movable line or a plotted function has one -- which is what makes a shape per category reach these points at all, rather than only the display's own. Seven Cypress assertions counted circles. The marquee is untouched: it indexes positions it computes itself, not the elements. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fd5e6a3 to
c66c838
Compare
Draws residual-plot points with the category's shape instead of always a circle. Seventh in the LEADS Point Shapes stack, on top of #2698.
Fixes CODAP-1526.
Why this exists separately
The residual plot draws its own SVG rather than going through a point renderer, so it is a fourth surface that draws a point — after the canvas renderer (#2696), PIXI (#2697) and the legend key (#2698). It stayed circles whatever the plot above it was drawn with. This is the last one.
The scope was wider than the code claimed
A comment in
use-residual-plot.tssaidresidualPlotIsApplicableexcludes legends, so the per-case legend lookup was dead. That is not what it does: it turns the plot off for a categorical legend only when the line is an LSRL. With a movable line or a plotted function a categorical legend is allowed, so the legend path is live — and per-category shapes genuinely reach these points, rather than only the display's own shape. The comment is corrected.Rendering
A point is a
<path>positioned withtranslate(cx, cy). The radius moves to adata-rattribute, because an outline has no radius to read back the way a circle does, and the existing fade-in machinery probes it.The grow animation regenerates the outline rather than scaling the point. A
scale()transform takes the stroke with it, and the stroke is what marks a point as selected — so a selected point would have faded in with an outline that thickened as it grew. Regenerating viaattrTweenleaves the whole existing fade-in intact: the named"radius"transition, theactive()guard, and the deliberate choice not to redirect a transition already past its starting phase.The shape comes from the same
getLegendShapeForCaselookup the main plot uses, so the two plots agree by construction.Testing
Seven Cypress assertions counted
circleelements and now count paths. The marquee is untouched — it indexes positions it computes itself rather than querying elements.No unit tests are added.
use-residual-plot.tshas none and this change does not create a testable seam: it is d3 rendering inside a hook with layout and context dependencies, and the pure functions it leans on (pointShapePathData,residualPointStyle) are already covered. Verified manually instead — including a point fading in and a selected point fading in, which is the case that distinguishes this approach from scaling.🤖 Generated with Claude Code