Skip to content

canvas: filtering to a subject area frames just that subset at a readable zoom - #58

Merged
dbose merged 1 commit into
mainfrom
fix-canvas-filter-fit-subset
Oct 18, 2026
Merged

dbose merged 1 commit into
mainfrom
fix-canvas-filter-fit-subset

Conversation

@dbose

@dbose dbose commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Problem

Ticking a subject-area filter (e.g. one domain of 12 — 6 of 79 entities) left the viewport zoomed to the whole model: the filtered cards showed as tiny specks spread across empty space, and with onlyRenderVisibleElements they could be culled entirely (0 nodes rendered).

Root cause

Every entity stays in the node array when filtering (hidden ones flagged, never removed), each keeping its full-model position. The subset was laid out compactly (dagre over the visible nodes), but the post-layout fitView() framed all 79 — so the filtered cards landed in a corner of that huge bounding box.

Fix

When filtering, frame only the visible subset: compute the bounding rect from the visible nodes' positions + known sizes and fitBounds() to it — the same position-based approach the search-framing effect already uses, because fitView({nodes}) can't measure an off-screen/culled node. Unfiltered behaviour is unchanged (fit all, only when no saved layout); clearing the filter re-fits the whole model.

Also: dagre acyclicer: 'greedy' so FK cycles (self-refs, mutual refs — common in reverse-engineered/erwin models) don't degrade the layered "sources higher, derived lower" ordering.

Verification

Reproduced on a 79-entity / 12-domain model (matching the reported erwin_import case):

  • Before: filter to a 6-entity domain → viewport fit to the 79-node extent, 0 nodes rendered (culled).
  • After: all 6 cards render legibly, framed at scale ~0.56; "Show all" re-fits the full model.

tsc clean; canvas Vitest 55 passed; ruff clean; server suite green.

Scope note

This is the critical usability repair (frame the subset). The fuller "base tables higher, facts/dims/aggregation lower" hierarchical-rank layout (declared-layer → topological-rank → FK-direction fallback, with a TB direction option) is a separate follow-up.

…able zoom

Ticking a subject-area filter (e.g. one domain of 12, 6 of 79 entities) left the viewport
zoomed to the WHOLE model: the filtered cards showed as tiny specks spread across empty
space, and with onlyRenderVisibleElements they could be culled entirely (0 nodes rendered).

Cause: every entity stays in the node array when filtering (hidden ones flagged, never
removed), each keeping its full-model position, and the post-layout fitView() framed all of
them. The subset WAS laid out compactly (dagre over the visible nodes), but the camera was
fit to the 79-node bounding box.

Fix: when filtering, frame only the visible subset. Compute the bounding rect from the
visible nodes' positions + known sizes and fitBounds() to it — the same position-based
approach the search-framing effect already uses, because fitView({nodes}) can't measure an
off-screen/culled node. Unfiltered behaviour is unchanged (fit all, only when no saved
layout). Clearing the filter re-fits the whole model.

Also: dagre acyclicer 'greedy' so FK cycles (self-refs, mutual refs) don't degrade the
layered 'sources higher, derived lower' hierarchy.

Verified live on a 79-entity / 12-domain model: filter to a 6-entity domain now renders all
6 cards legibly framed (scale ~0.56, 6 nodes rendered vs 0 before); Show all re-fits the
full model. tsc clean; canvas Vitest 55 passed.
@dbose
dbose merged commit d4b9937 into main Oct 18, 2026
2 checks passed
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