forked from 1b-agent/opencadstudio-upstream
feat(viewport): per-tile model render mode, persisted via VPORT entries
Each model-space tile now carries its own visual style instead of one global mode shared by every pane: - ModelTile gains a `render_mode`; the active tile renders with the live picker mode, inactive tiles keep their stored style, so changing one tile no longer changes the others. Split inherits the source style. - SetRenderMode writes only the active tile; switching the active tile mirrors its style back into the tab (picker + live render). - Persisted through the *Active VPORT entries (acadrust now round-trips the render mode via DXF code 281 / DWG RC 281), restored on open for both tiled and single-tile model layouts. - Bump acadrust to pick up the VPORT render-mode field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
addf4e3904
commit
f0b11acc88
3 files changed
+96
-3
No files matched your search
Generated
+2
-2
@@ -54,7 +54,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
||||
[[package]]
|
||||
name = "acadrust"
|
||||
version = "0.3.4"
|
||||
source = "git+https://github.com/HakanSeven12/acadrust?branch=main#b6834f22c6761b253f3753b26e2b6aefc23f4ff6"
|
||||
source = "git+https://github.com/HakanSeven12/acadrust?branch=main#5b09ccc00d92bb55dcccbef97c0fcecf94417e16"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -3176,7 +3176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d"
|
||||
dependencies = [
|
||||
"bytecount",
|
||||
"memchr 1.0.2",
|
||||
"memchr 2.8.1",
|
||||
"nom 8.0.0",
|
||||
]
|
||||
|
||||
|
||||
@@ -230,6 +230,7 @@ impl OpenCADStudio {
|
||||
// selected — see #21.
|
||||
self.sync_ribbon_from_selection();
|
||||
self.tabs[i].scene.restore_saved_camera();
|
||||
self.sync_render_mode_to_active_tile(i);
|
||||
self.tabs[i].last_synced_camera_gen = self.tabs[i].scene.camera_generation;
|
||||
self.tabs[i].dirty = false;
|
||||
self.tabs[i].history = super::document::HistoryState::default();
|
||||
@@ -704,6 +705,10 @@ impl OpenCADStudio {
|
||||
return Task::none();
|
||||
}
|
||||
self.tabs[i].render_mode = mode;
|
||||
// Write the style onto the active Model tile alone so it
|
||||
// sticks when that tile loses focus and the other tiles keep
|
||||
// their own styles.
|
||||
self.tabs[i].scene.set_active_model_tile_render_mode(mode);
|
||||
// Keep the legacy `wireframe` bool synced — both wireframe
|
||||
// modes set it, everything else clears it.
|
||||
let wf = matches!(mode, M::Wireframe2D | M::Wireframe3D);
|
||||
@@ -2024,6 +2029,7 @@ impl OpenCADStudio {
|
||||
.set_active_model_tile_at(p.x / vp_size.0, p.y / vp_size.1)
|
||||
{
|
||||
self.tabs[i].scene.camera_generation += 1;
|
||||
self.sync_render_mode_to_active_tile(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2386,6 +2392,7 @@ impl OpenCADStudio {
|
||||
.set_active_model_tile_at(p.x / vw, p.y / vh)
|
||||
{
|
||||
self.tabs[i].scene.camera_generation += 1;
|
||||
self.sync_render_mode_to_active_tile(i);
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
@@ -6056,6 +6063,35 @@ impl OpenCADStudio {
|
||||
/// `grip_hover` while the cursor sits within `GRIP_THRESHOLD_PX` of
|
||||
/// a grip and opens `grip_popup` once the dwell exceeds the
|
||||
/// threshold. Cursor drift clears both.
|
||||
/// After the active Model tile changes, mirror its stored visual style
|
||||
/// into the tab so the picker shows it and the tile renders with it
|
||||
/// (the active tile draws with the tab's live render mode).
|
||||
fn sync_render_mode_to_active_tile(&mut self, i: usize) {
|
||||
use acadrust::entities::ViewportRenderMode as M;
|
||||
if self.tabs[i].scene.current_layout != "Model" {
|
||||
return;
|
||||
}
|
||||
let mode = self.tabs[i].scene.active_model_tile_render_mode();
|
||||
if self.tabs[i].render_mode == mode {
|
||||
return;
|
||||
}
|
||||
let label = match mode {
|
||||
M::Wireframe2D => "Wireframe 2D",
|
||||
M::Wireframe3D => "Wireframe 3D",
|
||||
M::HiddenLine => "Hidden Line",
|
||||
M::FlatShaded => "Flat Shaded",
|
||||
M::GouraudShaded => "Gouraud Shaded",
|
||||
M::FlatShadedWithEdges => "Flat Shaded + Edges",
|
||||
M::GouraudShadedWithEdges => "Gouraud Shaded + Edges",
|
||||
};
|
||||
self.tabs[i].render_mode = mode;
|
||||
let wf = matches!(mode, M::Wireframe2D | M::Wireframe3D);
|
||||
self.tabs[i].wireframe = wf;
|
||||
self.ribbon.set_wireframe(wf);
|
||||
self.tabs[i].visual_style = label.into();
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
}
|
||||
|
||||
fn update_grip_hover(&mut self, i: usize, p: iced::Point) {
|
||||
const HOVER_OPEN_MS: u128 = 600;
|
||||
const POPUP_DISMISS_PX: f32 = 80.0;
|
||||
|
||||
+58
-1
@@ -449,6 +449,9 @@ pub struct ViewportInstance {
|
||||
pub(crate) struct ModelTile {
|
||||
pub(crate) rect: iced::Rectangle,
|
||||
pub(crate) camera: Camera,
|
||||
/// Visual style for this tile alone — each pane carries its own so
|
||||
/// changing one tile's render mode never touches the others.
|
||||
pub(crate) render_mode: acadrust::entities::ViewportRenderMode,
|
||||
}
|
||||
|
||||
/// Tolerance for matching two normalized tile coordinates as "the same"
|
||||
@@ -684,6 +687,7 @@ impl Scene {
|
||||
height: 1.0,
|
||||
},
|
||||
camera: Camera::default(),
|
||||
render_mode: acadrust::entities::ViewportRenderMode::Wireframe2D,
|
||||
}]),
|
||||
active_model_tile: std::cell::Cell::new(0),
|
||||
selection: Rc::new(RefCell::new(SelectionState::default())),
|
||||
@@ -2809,6 +2813,30 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
|
||||
/// Visual style of the active Model tile (for the render-mode picker).
|
||||
pub fn active_model_tile_render_mode(
|
||||
&self,
|
||||
) -> acadrust::entities::ViewportRenderMode {
|
||||
let tiles = self.model_tiles.borrow();
|
||||
let active = self.active_model_tile.get().min(tiles.len().saturating_sub(1));
|
||||
tiles
|
||||
.get(active)
|
||||
.map(|t| t.render_mode)
|
||||
.unwrap_or(acadrust::entities::ViewportRenderMode::Wireframe2D)
|
||||
}
|
||||
|
||||
/// Set only the active Model tile's render mode. Other tiles keep theirs.
|
||||
pub fn set_active_model_tile_render_mode(
|
||||
&self,
|
||||
mode: acadrust::entities::ViewportRenderMode,
|
||||
) {
|
||||
let mut tiles = self.model_tiles.borrow_mut();
|
||||
let active = self.active_model_tile.get().min(tiles.len().saturating_sub(1));
|
||||
if let Some(t) = tiles.get_mut(active) {
|
||||
t.render_mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
/// View-rotation matrix for the active viewport (MSPACE), or the
|
||||
/// paper-space camera's matrix when not in MSPACE.
|
||||
/// Used by ViewCube hit-testing so clicks map to the correct camera.
|
||||
@@ -4726,6 +4754,16 @@ impl Scene {
|
||||
/// Set the model-space camera from the VPORT table's *Active entry.
|
||||
/// Returns true if the entry was found and the camera was set.
|
||||
fn apply_active_vport_camera(&mut self) -> bool {
|
||||
// Restore the single tile's visual style from the *Active entry,
|
||||
// independent of where the camera itself comes from below.
|
||||
if let Some(vp) = self.document.vports.iter().find(|v| v.name == "*Active") {
|
||||
let mode = vp.render_mode;
|
||||
let mut tiles = self.model_tiles.borrow_mut();
|
||||
let active = self.active_model_tile.get().min(tiles.len().saturating_sub(1));
|
||||
if let Some(t) = tiles.get_mut(active) {
|
||||
t.render_mode = mode;
|
||||
}
|
||||
}
|
||||
// Prefer our named View entry — survives DWG save without being overridden.
|
||||
let saved_view = self
|
||||
.document
|
||||
@@ -4874,6 +4912,7 @@ impl Scene {
|
||||
self.camera_from_vport(vp).map(|cam| ModelTile {
|
||||
rect: Self::vport_to_tile_rect(vp.lower_left, vp.upper_right),
|
||||
camera: cam,
|
||||
render_mode: vp.render_mode,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
@@ -4935,6 +4974,7 @@ impl Scene {
|
||||
for tile in ordered_tiles {
|
||||
let (ll, ur) = Self::tile_rect_to_vport(tile.rect);
|
||||
let mut entry = self.vport_from_camera("*Active", &tile.camera, ll, ur);
|
||||
entry.render_mode = tile.render_mode;
|
||||
entry.handle = self.document.allocate_handle();
|
||||
self.document.vports.add_allow_duplicate(entry);
|
||||
}
|
||||
@@ -5614,15 +5654,18 @@ impl Scene {
|
||||
},
|
||||
)
|
||||
};
|
||||
let mode = tiles[active].render_mode;
|
||||
tiles[active] = ModelTile {
|
||||
rect: a,
|
||||
camera: cam_now.clone(),
|
||||
render_mode: mode,
|
||||
};
|
||||
tiles.insert(
|
||||
active + 1,
|
||||
ModelTile {
|
||||
rect: b,
|
||||
camera: cam_now,
|
||||
render_mode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -5669,11 +5712,21 @@ impl Scene {
|
||||
/// tile becomes active. Used by VPORTS presets and `reset_model_tiles`.
|
||||
pub fn set_model_tile_layout(&self, rects: Vec<iced::Rectangle>) {
|
||||
let cam_now = self.camera.borrow().clone();
|
||||
// Every new pane inherits the active tile's current visual style.
|
||||
let mode = {
|
||||
let tiles = self.model_tiles.borrow();
|
||||
let active = self.active_model_tile.get().min(tiles.len().saturating_sub(1));
|
||||
tiles
|
||||
.get(active)
|
||||
.map(|t| t.render_mode)
|
||||
.unwrap_or(acadrust::entities::ViewportRenderMode::Wireframe2D)
|
||||
};
|
||||
let tiles: Vec<ModelTile> = rects
|
||||
.into_iter()
|
||||
.map(|rect| ModelTile {
|
||||
rect,
|
||||
camera: cam_now.clone(),
|
||||
render_mode: mode,
|
||||
})
|
||||
.collect();
|
||||
*self.model_tiles.borrow_mut() = if tiles.is_empty() {
|
||||
@@ -5685,6 +5738,7 @@ impl Scene {
|
||||
height: 1.0,
|
||||
},
|
||||
camera: cam_now,
|
||||
render_mode: mode,
|
||||
}]
|
||||
} else {
|
||||
tiles
|
||||
@@ -5751,7 +5805,10 @@ impl Scene {
|
||||
height: tile.rect.height * canvas_h,
|
||||
},
|
||||
camera,
|
||||
render_mode: model_mode,
|
||||
// The active tile shows the live mode the picker
|
||||
// drives; every other tile keeps its own stored
|
||||
// style so editing one never disturbs the rest.
|
||||
render_mode: if i == active { model_mode } else { tile.render_mode },
|
||||
active: i == active,
|
||||
paper_sheet: false,
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user