diff --git a/src/app/view.rs b/src/app/view.rs index 867cdd635..58712e8fe 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -457,61 +457,22 @@ impl H7CAD { // ── Paper canvas ────────────────────────────────────────────────────────── // -// In paper space, the main canvas is composed of: -// 1. A full-size PaperSheet layer — renders paper-space entities (title blocks, -// frames, borders) with the paper-space camera. -// 2. One ViewportPane::Paper widget per Viewport entity, positioned at its -// paper-space coordinates and sized to its paper-space dimensions. -// Each renders model-space content through its own viewport camera. -// 3. The existing selection_overlay / viewport_mouse / nav stack on top -// (unchanged — mouse event routing stays the same). +// Renders the full paper canvas as a single full-size shader widget using the +// PaperSheet mode, which includes both paper-space entities (title blocks, +// frames, borders) and model-space content projected through each viewport's +// view matrix into paper-space coordinates. +// +// Note: A per-viewport widget approach (ViewportPane::Paper) was attempted but +// does not work correctly because Iced 0.14 batches all shader prepare() calls +// before any render() calls, causing widgets that share the same Pipeline type +// to overwrite each other's GPU buffers. The CPU-projection approach used here +// is the correct solution within Iced's shader framework. fn paper_canvas_view<'a>(tab: &'a super::document::DocumentTab) -> Element<'a, Message> { - let scene = &tab.scene; - - // Layer 1: paper-space entities (title blocks, frames, paper boundary). - let paper_sheet = shader(ViewportPane::paper_sheet(scene)) + shader(ViewportPane::paper_sheet(&tab.scene)) .width(Fill) - .height(Fill); - - // Use the last-rendered canvas size to compute pixel positions. - // On the very first frame this is (0,0); correct positions appear from - // the second frame onward (imperceptible in practice). - let (canvas_w, canvas_h) = scene.selection.borrow().vp_size; - let vp_list = scene.viewport_list(); - - let mut layers = stack![paper_sheet]; - - for (handle, _, _) in &vp_list { - let Some(rect) = scene.viewport_screen_rect(*handle, (canvas_w, canvas_h)) else { - continue; - }; - - // Clamp to avoid zero-size or negative widgets. - let w = rect.width.max(1.0); - let h = rect.height.max(1.0); - let x = rect.x.max(0.0); - let y = rect.y.max(0.0); - - // Layer 2+: one shader widget per viewport, positioned with Space offsets. - let vp_widget = shader(ViewportPane::paper(scene, *handle)) - .width(iced::Length::Fixed(w)) - .height(iced::Length::Fixed(h)); - - let positioned = column![ - Space::new().height(iced::Length::Fixed(y)), - row![ - Space::new().width(iced::Length::Fixed(x)), - vp_widget, - ], - ] - .width(Fill) - .height(Fill); - - layers = layers.push(positioned); - } - - layers.width(Fill).height(Fill).into() + .height(Fill) + .into() } // ── Document tab bar ─────────────────────────────────────────────────────── diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 22286c268..39ffef8d3 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -2103,9 +2103,9 @@ impl Scene { /// `Rectangle` relative to the top-left of the paper canvas. /// /// `canvas_px` — the pixel dimensions of the area that shows the paper. - /// Paper limits from the current layout define the paper-space extent; - /// the viewport is mapped linearly into that area (Y-axis flipped). - /// Returns `None` if the viewport handle is invalid or paper limits are missing. + /// Map a paper-space viewport into screen-pixel coordinates. + /// Needed when per-viewport shader widgets are revived (see `ViewportPaneMode::Paper`). + #[allow(dead_code)] pub fn viewport_screen_rect( &self, vp_handle: Handle, diff --git a/src/scene/render.rs b/src/scene/render.rs index 31a8abdbc..bc14f3b24 100644 --- a/src/scene/render.rs +++ b/src/scene/render.rs @@ -196,10 +196,9 @@ impl Scene { } } - /// Build a Primitive for the paper-sheet background: paper-space entities - /// (title blocks, frames, borders) using the paper-space camera. - /// Viewport content is NOT included — it is rendered by separate - /// `ViewportPane::Paper` widgets stacked on top. + /// Build a Primitive for the full paper canvas: paper-space entities + /// (title blocks, frames, borders) plus model-space content projected + /// through each viewport's view matrix into paper-space coordinates. pub(super) fn build_paper_sheet_primitive( &self, hover_region: Option, @@ -208,7 +207,9 @@ impl Scene { let cam = self.camera.borrow(); self.selection.borrow_mut().vp_size = (bounds.width, bounds.height); + let layout_block = self.current_layout_block_handle(); let mut wires = self.paper_sheet_wires(); + wires.extend(self.viewport_content_wires(layout_block, None)); if let Some(iw) = &self.interim_wire { wires.push(iw.clone()); } diff --git a/src/scene/viewport_pane.rs b/src/scene/viewport_pane.rs index d436b1c49..20a8cc056 100644 --- a/src/scene/viewport_pane.rs +++ b/src/scene/viewport_pane.rs @@ -9,10 +9,17 @@ use iced::{mouse, Event, Rectangle}; pub enum ViewportPaneMode { /// Full model space — fills whatever bounds Iced assigns. Model, - /// Paper-space entities only (title blocks, frames, borders) using the - /// paper-space camera. No viewport content projection. + /// Paper-space entities plus model content projected through viewports. + /// Used for the full paper canvas (single widget, no per-viewport widgets). PaperSheet, - /// Model-space content seen through a specific paper-space Viewport entity. + /// Model-space content rendered through a specific viewport's 3-D camera. + /// + /// NOTE: Currently unused because Iced 0.14 batches all shader `prepare()` + /// calls before any `render()` calls. Multiple widgets sharing the same + /// `Pipeline` type overwrite each other's GPU buffers. A per-viewport + /// wgpu sub-renderer that accumulates data across frames would be needed to + /// revive this path. + #[allow(dead_code)] Paper { handle: Handle }, } @@ -34,6 +41,8 @@ impl<'a> ViewportPane<'a> { } /// One paper-space viewport: model content rendered through its own camera. + /// See [`ViewportPaneMode::Paper`] for why this is currently unused. + #[allow(dead_code)] pub fn paper(scene: &'a Scene, handle: Handle) -> Self { Self { scene, mode: ViewportPaneMode::Paper { handle } } }