Enhancements: Z-order issue#223
Conversation
…hange to ensure correct rendering
… consistent badge rendering - Added a new private property to track duplicate groups across different projections. - Implemented a method to compute and unify duplicate groups based on exact coordinates across projections. - Updated relevant methods to utilize the new grouping for rendering and interaction, ensuring consistent badge display for duplicates.
|
next: add docs for the changes (especially badges) |
|
@tsenoner ready to merge |
|
check the docs page, Using the Explore Page -> Navigating the Scatterplot -> Duplicate Points for the gif |
|
Hi @peymanvahidi, Bug: Only the top-most point of the duplicate group renders at the expected position — the other duplicate points appear elsewhere on the plot. This may be a UMAP-specific issue where overlapping/duplicate points aren't rendered at the same coordinates. Could you investigate and propose a fix? See the screen recording below: Screen.Recording.2026-05-12.at.19.28.46.movAdditional request: ESC should also close an open duplicate badge, mirroring the behavior we have for selections. Proposed precedence:
|
|
A few things still missing before this is ready:
|
Drop the union-find that grouped points sharing coords in ANY projection. Under UMAP and t-SNE, members of a cross-projection group can render at distinct canvas coordinates, so the spider would open at one anchor while the other group members remained visible as scattered dots elsewhere. Now keyed by exact coords in the current projection only: identical embeddings still stack under PCA (where they collapse to the same point), but UMAP/t-SNE no longer fabricate stacks for points that are physically apart on screen. Extracted into duplicate-stack-helpers so the key contract can be unit-tested. Preserves the _cancelDuplicateStackCompute guard and the _scheduleDuplicateOverlayUpdate(true) call after quadtree rebuild — those fix the original #121 lag and are independent of this change.
_toggleSpiderfy previously wrote the click anchor directly onto the
current stack object's x/y/px/py. _duplicateStackByKey rebuilds with
fresh objects on every viewport recompute, so any pan or zoom while
the spider was open snapped the anchor back to whichever group member
the iterator happened to encounter first.
Store the anchor separately as { stackKey, x, y } on the component
and reapply it inside _ensureDuplicateStacksForViewport after each
rebuild. The reapply is guarded by stackKey match, so stale anchors
from a previously-closed stack stay inert.
…'t lie about positions WebGLRenderer's sort gate was tied to positionsDirty plus a sample-based depth-changed check. The check compared this.depths[i] (sorted-order value) against getDepth(points[i]) (input-order value), so it could not reliably detect category-level z-order swaps. The scatter-plot used to work around this by calling invalidatePositionCache() on z-order change, which forced a re-sort by claiming the position buffer was stale. Add a separate depthOrderDirty flag and a public invalidateDepthOrder() method on WebGLRenderer. populateBuffers honors the flag and clears it after re-sorting. scatter-plot's _handleZOrderChange and the !colorOnly path of _handleColorMappingChange now call invalidateDepthOrder() — positions remain valid, but the painter-sort is rebuilt.
…ring selection Mirror the selection ESC cascade: pressing Escape with a duplicate-badge spider open now collapses the spider first, leaving the rest of the selection state untouched. A subsequent Escape clears selections as before, and a third toggles off selection mode. Expose hasExpandedDuplicateStack() and closeExpandedDuplicateStack() from the scatter-plot, extend the control-bar's ScatterplotElementLike interface accordingly, and prepend the badge-close branch to the existing handleDocumentKeydown cascade.
…switch regressions Extend duplicate-stack-helpers with buildDuplicateStacks (a sync helper that mirrors the chunked production algorithm) and the DuplicateStackPoint type, then add Vitest coverage for the two failure modes from #121: - legend-hide: stacks must shrink, then disappear, as visible points are filtered out — proving the duplicate pass respects opacity-based filtering applied upstream in _buildQuadtree. - projection-switch: the same proteins under different projection coords must produce different stack sets — no leakage from the previous projection's groupings. Also pins the UMAP-jitter case explicitly: two points with identical embeddings but different UMAP coords must NOT form a stack, which is the failure mode the cross-projection union-find used to produce.
|
Addressed all items from your review:
#101 note: per-projection grouping means UMAP/t-SNE generally don't show duplicate badges (they don't co-locate identical embeddings). PCA still does. Happy to revisit if you'd prefer "badges by embedding identity regardless of projection," but that's the behavior that produced the visual misbehavior you recorded. |
…orgot to commit this change)
|
Why are the same sequences in UMAP not overlapping as they do in PCA? |
|
I think because it's stochastic. |
|
Yeah, came to the same conclusion. So we just have to keep in mind that, for the UMAP, duplicated badges might not serve any purpose. Which is fine. |
Untested below the verified 1440-1920px range, the clamp() shrank 1rem to 12px on smaller viewports (incl. the 1280x720 Playwright config), affecting all rem-based sizing across the app. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This PR fixes the issues below:
Notes
#101 status. Duplicate badges are now grouped per the current projection: two points stack iff they share exact coords in the displayed view. PCA collapses identical embeddings to the same coordinates, so badges appear there. UMAP/t-SNE generally don't co-locate them, so badges don't appear under those projections.
html { font-size: clamp(0.75rem, 0.87vw, 1.125rem) }inapp/src/index.css. Fluid base font size, 12px to 18px between ~1379–2069px viewport width. Intent: improve readability on wider displays. Tested on standard desktop viewports (~1440–1920px, MacBook 14"/16" + external monitor).ESC cascade. ESC priority is now: (1) close open duplicate-badge spider, (2) clear selection, (3) toggle off selection mode.
New renderer API.
WebGLRenderer.invalidateDepthOrder()lets callers signal a depth-sort invalidation without invalidating the position cache. Used by scatter-plot's z-order/color-mapping handlers.