fix(viewport): apply per-viewport layer freeze to fills, images and meshes

A content viewport that froze layers still drew those layers' hatches,
2-D solids, images, OLE frames and 3-D solids — only its wires honoured
`Viewport.frozen_layers`. A viewport meant to be empty (all content
layers frozen) leaked the whole model instead.

viewport_data_for fed content viewports the full model hatch/wipeout/
image/mesh sets with no per-viewport frozen filter. Thread the viewport's
frozen-layer set through the builders (synced_hatch_models,
exploded_insert_hatch_models, wipeout_models, image_models, mesh_models,
instanced_block_meshes) via Scene::layer_frozen_in — the same
layer-name→handle→membership test the wire path uses — and expose
*_for_viewport accessors that cache the filtered set per order-independent
frozen-set signature (viewports sharing a frozen set share the build; an
empty set returns the shared unfiltered arc, so model tiles and
non-freezing viewports are byte-identical).

Each viewport renders in its own pipeline slot with the hatch upload
gated on geometry_epoch, so a per-viewport filtered set uploads once per
epoch rather than every frame.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-20 19:01:30 +03:00
1 parent fd33810057
commit b884e33f38
4 files changed
+222 -28

No files matched your search

+23 -3
View File
@@ -1289,6 +1289,23 @@ impl Scene {
// bleed past the viewport borders whenever model coords overlap the
// paper area. Content viewports keep the full set (the model camera +
// per-viewport scissor place / clip them correctly).
// Per-viewport frozen-layer set for a paper content viewport. Content
// viewports must hide FILLS / IMAGES / SOLIDS on their frozen layers too,
// not just wires (which `model_wires_for_viewport_arc` already filters).
// Empty for the sheet, model tiles and the implicit-model view — none of
// which carry a per-viewport freeze — so those reuse the shared sets.
let vp_frozen: rustc_hash::FxHashSet<Handle> = if !inst.paper_sheet
&& inst.tile_idx.is_none()
&& inst.handle != acadrust::Handle::NULL
{
match self.document.get_entity(inst.handle) {
Some(EntityType::Viewport(vp)) => vp.frozen_layers.iter().cloned().collect(),
_ => rustc_hash::FxHashSet::default(),
}
} else {
rustc_hash::FxHashSet::default()
};
let (hatches, wipeout_hatches) = if inst.paper_sheet {
let mut v: Vec<HatchModel> = Vec::new();
if let Some(sheet) = self.paper_sheet_fill() {
@@ -1297,12 +1314,15 @@ impl Scene {
v.extend(self.paper_canvas_hatches().iter().cloned());
(Arc::new(v), self.paper_canvas_wipeouts())
} else {
(self.hatch_models_arc(), self.wipeout_models_arc())
(
self.hatch_models_for_viewport(&vp_frozen),
self.wipeout_models_for_viewport(&vp_frozen),
)
};
let images = if inst.paper_sheet {
self.paper_sheet_images()
} else {
self.images_arc()
self.images_for_viewport(&vp_frozen)
};
// The paper sheet shows the layout's own 2-D content (fills, borders,
// annotation) — never the model's 3-D solids. Those are drawn inside
@@ -1314,7 +1334,7 @@ impl Scene {
let meshes = if inst.paper_sheet {
Arc::new(Vec::new())
} else {
self.meshes_arc()
self.meshes_for_viewport(&vp_frozen)
};
// SDF text quads (behind OCS_TEXT_SDF). The glyph quads ride on each