Files
OneCAD-Draft/VIEWPORT_WIDGET_PLAN.md
T
Hakan SevenandClaude Sonnet 4.6 da9fafc0f8 feat: introduce ViewportPane widget for unified model/paper rendering
- Remove Scene: shader::Program<Msg> impl; all GPU rendering now goes
  through ViewportPane which supports three modes:
    Model      — full model-space view (unchanged behaviour)
    PaperSheet — paper-space entities only, using the paper camera
    Paper      — model content through a specific viewport's own camera

- Add Scene helper methods (render.rs):
    build_primitive / build_paper_sheet_primitive / build_viewport_primitive
    update_viewcube_state / viewcube_mouse_interaction

- Add Scene helpers (mod.rs):
    paper_sheet_wires()        — paper entities without viewport projection
    camera_for_viewport()      — derive Camera from Viewport entity data
    model_wires_for_viewport() — model wires filtered by per-viewport frozen layers
    viewport_screen_rect()     — paper-space → pixel rect mapping

- Add paper_canvas_view() in view.rs:
    Stack of PaperSheet + one Paper widget per Viewport entity,
    positioned with Space offsets from viewport_screen_rect()

- Update VIEWPORT_WIDGET_PLAN.md: mark Steps 1–4 done, document remaining work

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 02:18:41 +03:00

144 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
CONTEXT PROMPT FOR AI ASSISTANT
=================================
You are working on H7CAD, a CAD application written in Rust using the Iced GUI framework and wgpu for GPU rendering. The project is at /home/hakanseven/Kodlama/H7CAD.
This document describes a planned refactoring. Steps 1–4 are DONE and committed. Steps 5–6 remain.
Key files to read before continuing:
- src/scene/viewport_pane.rs — NEW: ViewportPane widget (Model / PaperSheet / Paper modes)
- src/scene/render.rs — Scene render helpers (build_primitive, build_paper_sheet_primitive, build_viewport_primitive)
- src/scene/mod.rs — Scene struct; viewport_screen_rect(), camera_for_viewport(), model_wires_for_viewport(), paper_sheet_wires()
- src/app/view.rs — paper_canvas_view() function; view() uses ViewportPane::model + paper_canvas_view
- src/scene/camera.rs — Camera struct (arcball, orthographic/perspective)
- src/entities/viewport.rs — Viewport DXF entity: grips, properties, transform impls
- src/ui/statusbar.rs — layout tab bar (space_tab function, LayoutSwitch message)
Architecture summary (after Steps 1–4):
- Model tab: shader(ViewportPane::model(&tab.scene)).width(Fill).height(Fill)
- Paper tab: paper_canvas_view(tab) which builds a stack![] of:
layer 1: shader(ViewportPane::paper_sheet(scene)) — paper-space entities, paper camera
layer 2+: shader(ViewportPane::paper(scene, handle)) per Viewport entity,
positioned with Space offsets computed from viewport_screen_rect()
- Mouse events in paper space still route through the same viewport_mouse mouse_area overlay.
- ViewCube is shown in Model and PaperSheet modes; hidden in Paper mode.
What still needs to be done (Steps 5–6):
5. Mouse routing per-viewport in paper space (MSPACE double-click to enter, Escape to exit).
Currently the viewport_mouse mouse_area covers the full canvas; MSPACE detection already
works via Scene::active_viewport + viewport_at_paper_point(). Needs wiring to route
pan/zoom/select to the correct viewport widget.
6. Per-viewport layer freeze already works in build_viewport_primitive via model_wires_for_viewport().
Verify that the existing frozen_layers list on each Viewport entity is respected correctly.
-->
# Unified ViewportPane Widget Plan
## Current State (as of implementation start)
- `src/scene/render.rs`: `Scene` implements `shader::Program<Msg>` → single full-screen GPU widget
- `src/app/view.rs:106`: `shader(&tab.scene).width(Fill).height(Fill)` — same widget for both model and paper space, only render content differs
- `src/entities/viewport.rs`: `Viewport` entity is a pure DXF data struct with no shader or widget of its own
## Target Architecture
```
Model tab → ViewportPane (mode: Model) — Fill × Fill
Paper tab → paper_canvas_view()
├── ViewportPane (mode: PaperSheet) — Fill × Fill (paper entities + camera)
├── ViewportPane (mode: Paper, VP1) — vp.width × vp.height px
└── ViewportPane (mode: Paper, VP2) — vp.width × vp.height px
```
---
## ✅ Step 1 — `ViewportPane` Struct
**File:** `src/scene/viewport_pane.rs`
```rust
pub enum ViewportPaneMode {
Model,
PaperSheet, // paper-space entities, paper camera, no viewport projection
Paper { handle: Handle }, // model content through a specific viewport's camera
}
pub struct ViewportPane<'a> { pub scene: &'a Scene, pub mode: ViewportPaneMode }
```
Implements `shader::Program<Msg>`. `Scene`'s own `shader::Program` impl has been **removed** — all rendering now goes through `ViewportPane`.
Scene helper methods added to `render.rs`:
- `build_primitive()` — model/full paper space render (was `draw()`)
- `build_paper_sheet_primitive()` — paper entities only, no viewport projection
- `build_viewport_primitive(vp_handle)` — model content through viewport camera
- `update_viewcube_state()` / `viewcube_mouse_interaction()`
Scene helper methods added to `mod.rs`:
- `paper_sheet_wires()` — paper-space entity wires without viewport content
- `camera_for_viewport(handle)` — build Camera from Viewport entity data
- `model_wires_for_viewport(handle)` — model wires filtered by viewport layer freeze
---
## ✅ Step 2 — Per-Viewport Camera
Implemented via `camera_for_viewport()` in `src/scene/mod.rs`. Derives a `Camera` from the Viewport entity's `view_direction`, `view_target`, and `view_height` each frame — no separate HashMap needed since `pan_active_viewport()` / `zoom_active_viewport()` already write back to the entity.
---
## ✅ Step 3 — Paper-Space Coordinates → Pixel Conversion
```rust
pub fn viewport_screen_rect(&self, vp_handle: Handle, canvas_px: (f32, f32)) -> Option<iced::Rectangle>
```
Added to `src/scene/mod.rs`. Uses `paper_limits()` as the paper extent and `scene.selection.borrow().vp_size` as the live canvas size.
---
## ✅ Step 4 — `paper_canvas_view` Function
Added to `src/app/view.rs`. Builds a `stack![]` with:
1. `shader(ViewportPane::paper_sheet(scene))` — full-size paper background
2. One `shader(ViewportPane::paper(scene, handle))` per viewport, positioned via Space offsets
3. `viewport_3d` in `view()` is now `paper_canvas_view(tab)` when `is_paper`
---
## Step 5 — Mouse Routing per Viewport (TODO)
Currently the `viewport_mouse` mouse_area covers the full canvas for both model and paper space. MSPACE detection already works via `Scene::active_viewport` + `viewport_at_paper_point()`. What remains:
- Double-click on a paper viewport → enter MSPACE (set `active_viewport`)
- Escape → exit MSPACE (clear `active_viewport`)
- Pan/zoom in MSPACE → route to `pan_active_viewport()` / `zoom_active_viewport()`
These are already partially wired — verify that MSPACE interactions reach the correct `ViewportPane::Paper` widget through the existing message handlers.
---
## Step 6 — Per-Viewport Layer Freeze Verification (TODO)
`build_viewport_primitive()` calls `model_wires_for_viewport()` which filters by `vp.frozen_layers`. Verify that the frozen layer handles match the layer handles in the document (they should since both come from the same `CadDocument`).
---
## Implementation Order
1. ✅ `ViewportPane` + render helpers — model tab behavior unchanged
2. ✅ Per-viewport camera — derived from entity each frame
3. ✅ `viewport_screen_rect()` — paper → pixel coordinate mapping
4. ✅ `paper_canvas_view()` — stack of PaperSheet + Paper widgets
5. **TODO** Mouse routing verification for MSPACE in paper space
6. **TODO** Per-viewport layer freeze verification
---
## Notes
- `CameraState` (viewcube hover) is per `ViewportPane` instance — Iced manages this automatically via `shader::Program::State`.
- Paper-space entities (title blocks, frames) are rendered by `ViewportPane::PaperSheet` using the same paper-space camera as before.
- The ViewCube is hidden in `ViewportPane::Paper` mode (only shown in Model and PaperSheet).
- `selection_overlay` and grip markers use paper-space screen coordinates — this is correct for PSPACE. For MSPACE, grips of model entities are projected through the paper camera, which may need adjustment in a future step.
- Known limitation: `viewport_screen_rect()` uses the last-rendered canvas size (`scene.selection.borrow().vp_size`), so viewport positions are correct from the second frame onward.