CHANNEL_EXPANDS_MODEL_BOUNDS in packages/renderer/src/renderer-overlays.ts decides whether a 3D line overlay grows the scene AABB, keyed by channel. But the content reaching a channel does not follow the channel name: the annotation channel can receive a buffer holding only IfcGrid geometry, and then it reframes the camera on grid axes, which is exactly what grid: false exists to prevent.
The failing case
apps/viewer/src/components/viewer/Viewport.tsx calls:
useSymbolicAnnotations({ enabled: ifcAnnotationsVisible, gridEnabled: ifcGridVisible, ... })
and apps/viewer/src/hooks/useSymbolicAnnotations.ts appends the grid buckets into the same verts array it returns for the annotation channel.
So with "Show IFC Annotations" off and the IfcGrid toggle on, the array delivered to setLineOverlay('annotation', ...) contains only grid lines. CHANNEL_EXPANDS_MODEL_BOUNDS.annotation is true, so expandModelBoundsWithFlatVertices and syncCameraSceneBounds run, and the camera reframes on the grid.
Grid axes routinely extend well past the model envelope. That is the documented reason grid: false exists (#967), and the same reason applies to the DXF reference layer (#2043).
Why the table cannot see it
The policy is keyed by channel; the real rule is about content — does this geometry define the model's extent, such that a file containing only it must still be framable. Those coincide for three of the four channels and diverge for annotation, because one hook multiplexes two content kinds into it.
Marked unproven: this is read off the source, not observed in a running viewer. Reproducing it means loading a file with IfcGrid axes extending past the products, turning annotations off and the grid on, and watching Home / fit-to-view.
Two possible fixes
- Route the symbolic grid lift into the
grid channel, so a channel name means one content kind. That changes which section-clip and visibility semantics apply to those lines, so it is not a rename.
- Key the policy by content contribution rather than by channel, so a buffer declares whether it defines extent.
The second is the deeper fix and the more expensive one.
Also worth recording
The boolean is expand-only. Clearing an expanding channel never shrinks the bounds back, so toggling annotations on grows them for the rest of the session. A Record<channel, boolean> cannot express that; per-channel extent contributions recomputed on set and clear could.
How this was found
During review of the line-overlay channel collapse. The code comment in renderer-overlays.ts names this gap and defers it; filing so it does not stay deferred by default. No code change has been made for it.
CHANNEL_EXPANDS_MODEL_BOUNDSinpackages/renderer/src/renderer-overlays.tsdecides whether a 3D line overlay grows the scene AABB, keyed by channel. But the content reaching a channel does not follow the channel name: theannotationchannel can receive a buffer holding only IfcGrid geometry, and then it reframes the camera on grid axes, which is exactly whatgrid: falseexists to prevent.The failing case
apps/viewer/src/components/viewer/Viewport.tsxcalls:and
apps/viewer/src/hooks/useSymbolicAnnotations.tsappends the grid buckets into the samevertsarray it returns for theannotationchannel.So with "Show IFC Annotations" off and the IfcGrid toggle on, the array delivered to
setLineOverlay('annotation', ...)contains only grid lines.CHANNEL_EXPANDS_MODEL_BOUNDS.annotationistrue, soexpandModelBoundsWithFlatVerticesandsyncCameraSceneBoundsrun, and the camera reframes on the grid.Grid axes routinely extend well past the model envelope. That is the documented reason
grid: falseexists (#967), and the same reason applies to the DXF reference layer (#2043).Why the table cannot see it
The policy is keyed by channel; the real rule is about content — does this geometry define the model's extent, such that a file containing only it must still be framable. Those coincide for three of the four channels and diverge for
annotation, because one hook multiplexes two content kinds into it.Marked unproven: this is read off the source, not observed in a running viewer. Reproducing it means loading a file with IfcGrid axes extending past the products, turning annotations off and the grid on, and watching Home / fit-to-view.
Two possible fixes
gridchannel, so a channel name means one content kind. That changes which section-clip and visibility semantics apply to those lines, so it is not a rename.The second is the deeper fix and the more expensive one.
Also worth recording
The boolean is expand-only. Clearing an expanding channel never shrinks the bounds back, so toggling annotations on grows them for the rest of the session. A
Record<channel, boolean>cannot express that; per-channel extent contributions recomputed on set and clear could.How this was found
During review of the line-overlay channel collapse. The code comment in
renderer-overlays.tsnames this gap and defers it; filing so it does not stay deferred by default. No code change has been made for it.